Spring学习笔记

Spring学习笔记

IOC
  • 控制反转,将原本程序中手动创建UserService对象的控制权,交由Spring框架管理

  • 简单说,就是把创建UserService对象控制权被反转到了Spring框架

DI 依赖注入
  • 就是在Spring创建这个对象的过程中,将这个对象所依赖的属性注入进去

Bean标签的一些常用的属性

  • id和name

    • 一般情况下,装配一个bean的时候,通过指定一个id属性作为Bean的名称
    • id属性在ioc容器中必须的唯一的
    • 如果bean的名称中有特殊字符,就需要使用name属性
  • class

    • class用于设置一个类的全路径,用Spring来生成这个类的实例和反射
image.png

由scope配置

  • singleton是默认值,单例

    • prototype,每一次都会创建一个新的实例,也就是说是多例的

Spring的注解方式注入

image.png
  • @Component、@Repository、@Service、@Comtroller等都是相当于在配置文件中对中的id的配置

    @Component("userService")//这就相当于是的id;
    
    
    
  • 如果是普通类型用@Value,这个注解给key设值

    /*
        正常这时候得需要一个set方法
        然后在配置文件中使用进行设值
         */
        @Value("米饭")
        private String something;
    
     /*
        如果提供了set方法,这时候注解需要添加在set方法前
         */
        @Value("米饭")
        public void setSomething(String something) {
            this.something = something;
        }
    
  • 如果是对象类型的话用一下方法,例如:UserDao的注解

image.png
@Autowired
    @Qualifier("userDao")
    private UserDao userDao;

@Repository("userDao")
public class UserDao {
    public void save(){
        System.out.println("Dao层保存数据");
    }
}
//其中的@Qualifier("userDao")和@Repository("userDao")中的userDao名称一定要一致,否则会报错


//
@Resource(name = "userDao")就相当于@Autowired   @Qualifier("userDao")  //Resource也是强制用名称注入
image.png

Spring的其他注解

image.png
@Component("bean1")
public class Bean1 {

    @PostConstruct
    public void init(){
        System.out.println("initBean......");
    }

    public void say(){
        System.out.println("sayBean.......");
    }

    @PreDestroy
    public void destory(){
        System.out.println("destoryBean..........");
    }
 =======================================================================================
@Component("bean2")
@Scope("prototype")
public class Bean2 {
}
======================================================================================
    @Test
    public void demo3(){
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Demo2 bean1 = (Demo2) applicationContext.getBean("demo2");
        Demo2 bean2 = (Demo2) applicationContext.getBean("demo2");

        System.out.println(bean1 == bean2);

    }
    ===================================================================================
  • 当bean2的Scope为默认值singleton的为单例的 ,bean1 == bean2的值为true;

  • 当bean2的Scope为prototype的为多例的 ,bean1 == bean2的值为false;

传统xml配置与注解配置混合使用

public class ProduceService {
  @Resource(name = "categoryDao")
  private CategoryDao categoryDao;

  @Resource(name = "productDao")
  private ProductDao productDao;

//    public void setCategoryDao(CategoryDao categoryDao) {
//        this.categoryDao = categoryDao;
//    }
//
//    public void setProductDao(ProductDao productDao) {
//        this.productDao = productDao;
//    }

  public  void  save(){
      System.out.println("ProduceServicede的save()方法被执行了");
      categoryDao.save();
      productDao.save();
  }
}
=========================================================================================
   
  


  

  
  

  
  • <>这个是启动属性注入

  • 正常是通过set方法

    public void setCategoryDao(CategoryDao categoryDao) {
    //        this.categoryDao = categoryDao;
    //    }
    //
    //    public void setProductDao(ProductDao productDao) {
    //        this.productDao = productDao;
    //    }
    
    • 然后使用xml配置文件配置

      
          
      
  • 启用<>后,可以通过注解方式来代替先创建set方法然后在xml配置文件中配置

    @Resource(name = "categoryDao")
        private CategoryDao categoryDao;
    
        @Resource(name = "productDao")
        private ProductDao productDao;
    

Spring AOP

public class UserDaoImpl implements UserDao{
    public void save(User user){
    public void checkPrivilege(){
        checkPrivilege()
        ...保存用户
    }
    
    public void update(User user){
        ...更新用户
    }
    
    public void delete(User user){
        ...删除用户
    }
    
    public List find( ){
        ...查询用户
    }
    
    public void checkPrivilege(){
        //检查权限
    }
}

  • 这个类中保存用户时,调用checkPrivilege()方法检查权限。但是这只是一个类,如果有很多个类,要再每一个来中都写这么一个方法然后调用时非常麻烦的
  • 传统方法:纵向继承

    public class BaseDaoImpl{
        public void checkPrivilege(){   
        }
    }
    
    public class UserDaoImpl extends BaseDaoImpl{
        public void save(User user){
            checkPrivilege()
            //保存用户
        }
    }
    
  • 没有使用AOP的方式:

  • 定义一个父类的实现类,父类中有一个检查权限的方法

  • 让其他的类继承这个父类的实现类,子类中可以有父类中的方法,直接调用checkPrivilege()检查权限

  • 使用AOP的动态代理方法:采用横向抽取机制

    • SpringAOP使用纯java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式想目标类织入增强代码
  • AOP的相关术语:
image.png
image.png
  • public class UserDaoImpl implements UserDao{
        public void save(User user){
        public void checkPrivilege(){
            checkPrivilege()
            ...保存用户
        }
        
        public void update(User user){
            ...更新用户
        }
        
        public void delete(User user){
            ...删除用户
        }
        
        public List find( ){
            ...查询用户
        }
        
        public void checkPrivilege(){
            //检查权限
        }
    }
    
  • Joinpoint(连接点):指的是可以拦截到的点

    • 增删改查这些方法都可以被增强,这些方法成为是连接点
  • Pointcut(切入点):指的是真正被拦截到的点

  • 指想对save()方法进行增强(做权限校验),save方法称为是切入点

  • Advice(通知):拦截后要做的事:

    • 对save()方法进行权限校验,权限校验的方法称之为通知
      • 通知有前置和后置以及环绕通知
      • 前置通知:比如说想在save方法之前做权限校验,这就叫前置通知
      • 后置通知:比如说要在delete方法做一个日志记录,这就叫后置通知
      • 环绕通知:在之前和之后都会做一些事情
  • Target(目标):被增强的对象,例如:UserDaoImpl

  • Weaving(织入):将Advice应用到Target的过程

  • 将权限校验应用到UserDaoImpl的save()方法中的整个过程

  • Proxy(代理对象):被应用了增强后,产生的一个代理对象

    • Aspect(切面):就是切入点和通知的组合

JDK的动态代理

  • 定义一个接口
public interface UserDao {
    public void save();

    public void delete();

    public void update();

    public void find();
}
  • 接口的实现类
public class UserDaoImpl  implements UserDao{

    public void save() {
        System.out.println("保存用户");
    }

    public void delete() {
        System.out.println("删除用户");
    }

    public void update() {
        System.out.println("修改用户");
    }

    public void find() {
        System.out.println("查询用户");
    }
}
  • 编写一个动态代理类

    /*
        这个构造方法的作用是获取到需要增强的对象
         */
        public MyjdkProxy(UserDao userDao){
            this.userDao = userDao;
        }
    =====================================================================================
    public Object createProxy(){//用createProxy()这个方法产生一个代理,首选jdk的动态代理,因为jdk的动态代理可以对实现了接口的类实现代理
            /*
            传入的参数:类加载器userDao.getClass().getClassLoader();实现的接口userDao.getClass().getInterfaces();第三个参数是一个接口InvocationHandler
             */
            Object proxy = Proxy.newProxyInstance(userDao.getClass().getClassLoader(),userDao.getClass().getInterfaces(),this);
            return proxy;
        }
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if ("save".equals(method.getName())){
                System.out.println("权限校验");
                return method.invoke(userDao,args);
            }
            return method.invoke(userDao,args);
        }
    ====================================================================================
        /*
        编写测试类,使用代理
        */
        UserDao userDao = new UserDaoImpl();
    
    
            UserDao proxy = (UserDao)new MyjdkProxy(userDao).createProxy();
            proxy.save();
            proxy.delete();
            proxy.update();
            proxy.find();
        }
    

CGLIB动态代理

  • 底层:生成了一个类来继承目标类
  • 对于不使用接口的业务层,无法使用jdk的动态代理

  • CGLIB采用非常底层字节码技术,可以为一个类创建字类,解决无接口代理问题

public Object createProxy(){
    //1.创建一个CGLIB的核心类
    Enhancer enhancer = new Enhancer();
    //2.设置父类
    enhancer.setSuperclass(object.getClass());
    //3.设置回调
    enhancer.setCallback(this);
    //4.生成代理
    Object proxy = enhancer.create;
    return proxy;
}
  • 代理知识总结:

    • Spring在运行期,生成动态代理对象,不需要特殊的编译器

    • Spring AOP的底层就是通过jdk的动态代理或者CGLIB动态代理技术,为目标Bean执行横向织入

      • 若目标对象实现了若干接口,spring使用jdk的动态代理:java.lang.reflect.Proxy类代理

      • 若目标对象没事实现任何的接口,spring使用CGLIB库生成目标对象的子类

    • 程序中应优先对接口创建代理,以便于程序的解耦合以及后期的维护

    • 标记为final的方法,不能被代理,因为无法进行覆盖重写:因为她没有办法去重写父类或者父接口上面的方法

      • JDK的动态代理,是针对接口生成子类,接口中的方法不能使用final修饰
      • CGLIB是针对目标类生成子类,因此类或方法不能使用final
    • Spring只支持方法连接点,不提供属性连接点

一般切面的代码案例

  • 定义StudentDao接口
public interface StudentDao {
    public void save();

    public void find();

    public void update();

    public void delete();
}
  • 接口的实现类,实现了接口StudentDao

    public class StudentDaoImpl implements StudentDao{
        public void save() {
            System.out.println("save执行了。。。。。。。");
        }
    
        public void find() {
            System.out.println("find执行了。。。。。。。");
        }
    
        public void update() {
            System.out.println("update执行了。。。。。。");
        }
    
        public void delete() {
            System.out.println("delete执行了。。。。。。");
        }
    }
    
  • 定义一个一般切面Advisor

    public class MyBeforeAdvice implements MethodBeforeAdvice{
        public void before(Method method, Object[] objects, Object o) throws Throwable {
            System.out.println("前置通知======================");
        }
    }
    
  • 配置文件中配置

    
        
    
        
        
    
        
        
             
            
            
            
            
            
        
    
image.png

Spring带有切入点的切面配置

  • 我们需要代理的对象
public class CustomerDao {
    public void find(){
        System.out.println("查询用户...");
    }

    public void save(){
        System.out.println("保存用户...");
    }

    public void update(){
        System.out.println("修改用户");
    }

    public void delete(){
        System.out.println("删除用户");
    }
  • 通知Advice
/*
环绕通知时最强大的一种通知类型,它可以阻止目标方法的执行
 */
public class MyArroundAdvice implements MethodInterceptor {

    public Object invoke(MethodInvocation invocation) throws Throwable {


        System.out.println("环绕前增强...");
        Object obj =  invocation.proceed();//用来执行目标方法,比如说原来的方法是执行保存,这句哈的意思就相当于执行保存的操作
        System.out.println("环绕后增强...");

        return obj;
    }
  • 配置

    

    
    

    
    
        
        
        
        
    

    
    
        
        
        
    

自动创建代理

image.png

BeanNameAutoProxyCreate举例

  • 基于Bean名称的自动代理的创建方式,根据目标类的名称产生代理

  • 先把前几个例子中的接口,接口实现类,通知等拷贝过来

    public class CustomerDao {
        public void find(){
            System.out.println("查询用户...");
        }
    
        public void save(){
            System.out.println("保存用户...");
        }
    
        public void update(){
            System.out.println("修改用户");
        }
    
        public void delete(){
            System.out.println("删除用户");
        }
    }
    
    public interface StudentDao {
        public void save();
    
        public void find();
    
        public void update();
    
        public void delete();
    }
    
    public class StudentDaoImpl implements StudentDao {
        public void save() {
            System.out.println("save执行了。。。。。。。");
        }
    
        public void find() {
            System.out.println("find执行了。。。。。。。");
        }
    
        public void update() {
            System.out.println("update执行了。。。。。。");
        }
    
        public void delete() {
            System.out.println("delete执行了。。。。。。");
        }
    }
    
public class MyBeforeAdvice implements MethodBeforeAdvice {

    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("前置通知======================");
    }
}
public class MyArroundAdvice implements MethodInterceptor {

    public Object invoke(MethodInvocation invocation) throws Throwable {


        System.out.println("环绕前增强...");
        Object obj =  invocation.proceed();//用来执行目标方法,比如说原来的方法是执行保存,这句哈的意思就相当于执行保存的操作
        System.out.println("环绕后增强...");

        return obj;
    }
}
  • 配置文件配置

        
        
        
    
        
        
        
    
        
        
            
            
        
    
    
    
    • 这种自动代理有缺陷,比如说只想增强其中的save方法该怎么做到呢,这是基于Bean名称的方式又不太合适了

DefalutAdvisorAutoProxyCreator举例

  • 默认的切面的自动代理的方式创建,这种方式可以基于切面的信息自动产生代理

  • 前几个步骤与上一个方法类似,只是在配置文件中不同

    
        
        
    
        
        
        
    
        
        
            
            
        
    
        
        
    
    • 配置了一个切面,因为是基于切面的信息自动产生代理
    • 在切面配置中,;这一步就是根据类中的方法来增强,由于是正则表达式,所有需要对.进行转义\

AspectJ的注解AOP开发

  • AspectJ简介
    • AspectJ是一个基于Java语言的AOP框架
    • Spring2.0以后新增了对AspectJ切点表达式的支持
    • @AspectJ是AspectJ1.5新增的功能,通过JDK5注解技术,允许直接在Bean类中定义切面
    • 新版本的Spring框架,都建议使用AspectJ方式来开发AOP


      image.png

为目标类,定义切面类

public class PeoductDao {
    public void save(){
        System.out.println("保存商品");
    }

    public void delete(){
        System.out.println("删除商品");
    }

    public void update(){
        System.out.println("修改商品");
    }

    public void findOne(){
        System.out.println("查询一个商品");

    }

    public void findAll(){
        System.out.println("查询所有的商品");
    }
/*
切面类
 */
@Aspect
public class MyAspect {
    @Before(value = "execution(* com.imooc.aspectJ.demo1.PeoductDao.save(..))")
    public void before(){
        System.out.println("前置通知");
    }

    

    

    
    

详细介绍Before前置通知

image.png

@AfterReturing 后置通知

    @AfterReturning(value = "execution(* com.imooc.aspectJ.demo1.PeoductDao.update(..))",returning = "re")
    public void afterReturing(Object re){//为什么要用Object,因为这个返回值有可能是任意类型的返回值,所以用Object;
        System.out.println("后置通知=============" + re);
    }
  • //为什么要用Object,因为这个返回值有可能是任意类型的返回值,所以用Object;

  • @AfterReturing 异常抛出通知

@Around环绕通知

image.png
  • 重点:如果不调用joinPoint.proceed();方法的话,目标方法会被拦截,即delete不会执行

通过@Pointcut为切点命名

  • 在每个通知内定义切点,会造成工作量大,不易维护,对于重复的切点,可以使用@Pointcut进行定义

  • 切点方法:private void 无参数方法。方法名为切点名;因为这个方法在类中没有什么实际的意义,也不用在别的方法中调用它,所以定义为private

  • 当通知多个切点时,可以使用||进行连接

基于AspectJ的xml方式的AOP开发

  • 通过配置的方式完成AOP的开发

  • public interface CustomerDao {
        public void save();
    
        public void findOne();
    
        public void fingAll();
    
        public void update();
    
        public void delete();
    }
    
  • public class CustomerDaoImpl implements CustomerDao {
        public void save() {
          System.out.println("保存客户");
        }
    
        public void findOne() {
            System.out.println("查找一个客户");
    
        }
    
        public void fingAll() {
            System.out.println("查找所有的客户");
        }
    
        public void update() {
            System.out.println("修改客户");
    
        }
    
        public void delete() {
            System.out.println("删除客户");
    
        }
    
  • 
     
     
       
    
image.png
image.png

image.png

JDBC Template

你可能感兴趣的:(Spring学习笔记)