SpringIoc、动态代理:jdk,CGLib

2.4 Bean的作用域

Bean的作用域——指的是通过Spring容器工厂创建的对象作用范围;我们可以通过在bean标签中添加scope属性设置bean的作用域

  • scope=“singleton” 表示设置当前bean为单例模式 (默认是饿汉式,也就是说在Spring容器初始化时就会完成对象的创建;当在bean标签中设置lazy-init="true"变为懒汉式)
  • scope=“prototype” 表示设置当前bean为非单例模式,每次从Spring容器获取对象都会创建一个新的对象
ScopeDescription
singleton单例模式,从创建之后一直存在于Spring容器
prototype创建的实例不会存入Spring容器,每次获取都会创建新的对象
request作用于一次web请求,在一次请求中多次从Spring容器获取的是相同的实例
session作用于一次web会话,在一次会话中多次从Spring容器获取的是相同的实例
application作用于服务器的生命周期
websocket作用于一个网络连接
  • 单例饿汉式

    <bean id="student" class="com.qfedu.entity.Student"></bean>
    <bean id="student" class="com.qfedu.entity.Student" scope="singleton"></bean>
    
  • 单例懒汉式

    <bean id="student" class="com.qfedu.entity.Student" scope="singleton" lazy-init="true"></bean>
    
  • 非单例模式:每次访问都是新对象

    <bean id="student" class="com.qfedu.entity.Student" scope="prototype"></bean>
    
2.5 Bean的生命周期方法

Bean的生命周期方法——bean对象在创建或销毁的时候绑定执行的方法

  • 在bean标签中通过init-method属性指定当前bean的初始化方法,通过destroy-method属性指定bean的销毁方法
  • 在Bean类中定义初始化方法和销毁方法(无参无返回值)

    public class Student {
    
        public void aaa(){
            System.out.println("~~~~init");
        }
        
        public void bbb(){
            System.out.println("~~~~destory");
        }
    
    }
    
  • 在spring配置文件的bean标签中配置初始化方法和销毁方法

    <bean id="student" class="com.qfedu.entity.Student" init-method="aaa" destroy-method="bbb" scope="singleton" ></bean>
    
2.6 自动装配

自动装配:Spring容器在实例化当前bean的时候可以自动从Spring容器中寻找类型匹配或者ID匹配的实例进行赋值

  • byType:根据当前Bean属性的类型在Spring容器中寻找匹配的实例;如果找不到则不赋值,如果找到多个则抛出异常;
  • byName:根据当前Bean属性的属性名在Spring容器中寻找id与当前属性名相同的实例;如果找不到则不赋值,如果找到了但类型不匹配则抛出异常;
  • byType

    <bean id="userDAOImpl" class="com.qfedu.dao.impl.UserDAOImpl"></bean>
    
    <bean id="userService" class="com....UserServiceImpl" autowire="byType"></bean>
    
  • byName

    <bean id="userDAO" class="com.qfedu.dao.impl.UserDAOImpl"></bean>
    <bean id="userDAOImpl2" class="com.qfedu.dao.impl.UserDAOImpl2"></bean>
    
    <bean id="userService" class="com....UserServiceImpl" autowire="byName"></bean>
    
2.7 Spring IoC工作原理

在这里插入图片描述

三、Spring IoC 注解配置

Spring IoC的使用,需要通过xml将类声明给Spring容器进行管理,从而通过Spring工厂完成对象创建及属性值的注入;

Spring除了提供基于XML的配置方式外,还提供了基于注解的配置方式——将类交给Spring容器管理可以不用配置XML文件,直接在类上添加对应的注解即可。

3.1 Spring框架部署
3.1.1 创建Maven工程

3.1.2 添加SpringIoC依赖
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.15</version>
</dependency>
3.1.3 创建Spring配置文件
  • 在resources中创建Spring配置文件 applicationContext.xml

  • 在配置文件中引入context命名空间

在这里插入图片描述

  • 在配置文件中声明使用注解配置:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
    
        <!--  声明使用注解配置  -->
        <context:annotation-config/>
        
        <!--  声明Spring工厂注解的扫描范围  -->
        <context:component-scan base-package="com.qfedu.entity"/>
        
    </beans>
    
3.2 Spring IoC常用注解
3.2.1 @Component
  • 类注解,声明此类被Spring容器管理,相当于XML配置的bean标签的功能
  • @Component(value = "book") value属性相当于bean标签的id属性,指定此类在Spring容器的唯一ID;value属性可以省略,如果省略则id默认为类名首字母改小写
  • 除了@Component注解声明类被Spring管理之外,@Service、@Controller、@Respository这三个注解都可以声明类被Spring容器管理
    • @Controller 声明控制器类被Spring容器管理,例如Servlet
    • @Service 声明业务逻辑类被Spring容器管理,例如UserServiceImpl、BookServiceImpl
    • @Respository 声明持久层类被Spring容器管理,例如UserDAO、BookDAO
    • @Component 声明一般类被Spring容器管理
3.2.2 @Scope
  • 类注解,用于声明当前类的作用域,相当于bean标签的scope属性
  • @Scope(“prototype”) 表示声明当前类为非单例模式(默认是单例模式)
3.2.3 @Lazy
  • 类注解,声明一个单例模式的bean为懒汉式
  • @Lazy(true) 声明当前单例模式的bean为懒汉式(默认是饿汉式)
3.2.4 @PostConstruct 和 @PreDestroy
  • @PostConstruct 方法注解,声明此方法为初始化方法,在创建对象调用构造器后执行
  • @PreDestroy 方法注解,声明此方法为实例销毁方法,在实例被GC回收之前执行
3.2.5 @Autowired
  • 属性注解、set方法注解,设置当前属性自动装配,匹配规则:首先根据byName的规则从Spring容器查询id与当前属性名相同的bean进行装配,如果没有找到或者找到的bean与当前属性类型不匹配,再根据byType的规则从Spring容器查询类型匹配的bean进行装填;如果还是没有找到但是@Autowired(required=true)则抛出异常,如果@Autowired(required=false)则设置为null;如果根据类型找到多个则跑出异常

    @Autowired
    private UserDAO userDAO;
    
  • 这时我们可以通过@Qualifier声明装配的bean的id

@Autowired
public void setUserDAO(@Qualifier("userDAOImpl2") UserDAO userDAO) {
    this.userDAO = userDAO;
}
3.2.6 @Resource
  • 属性注解,作用与@Autowired相当;
  • 首先根据byName的规则从Spring容器查询id与当前属性名相同的bean进行装配,如果没有找到再根据byType的规则从Spring容器查询类型匹配的bean进行装填;如果根据ID找到的bean与当前属性类型不匹配则抛出异常

四、代理设计模式

4.1 生活中代理

4.2 静态代理

在这里插入图片描述

使用代理的好处

  1. 被代理类中只用关注核心业务的实现,将通用的管理型逻辑(事务管理、日志管理)和业务逻辑分离
  2. 将通用的代码放在代理类中进行实现,提高了代码的复用性
  3. 通过在代理类中添加业务逻辑,可以实现不改变原有代码的情况下实现业务增强
4.3 动态代理

动态代理——几乎可以为所有的类产生代理对象

动态代理的实现方式有2种

  • JDK动态代理:是通过实现被代理类实现的接口创建代理对象的,JDK动态代理要求被代理类必须实现接口
  • CGLib动态代理:是通过继承被代理类创建代理对象的,CGLib不能为final类产生代理对象
4.3.1 JDK动态代理

1.创建JDK动态代理类

/**
 * @Description JDK动态代理类
 * JDK自带动态代理需要实现最少一个接口
 * JDK动态代理类创建规则:
 * 1.实现InvocationHandler接口,同时实现invoke方法
 * 2.在类中定义一个Object类型变量obj,提供有参构造器,用于将被代理对象传递进来
 * 3.定义一个getProxy方法返回代理对象
 */
public class JDKDynamicProxy implements InvocationHandler {

    //被代理对象
    private Object obj;
    //有参构造器
    public JDKDynamicProxy(Object obj) {
        this.obj = obj;
    }

    public Object getProxy(){
        ClassLoader classLoader = obj.getClass().getClassLoader();
        Class<?>[] interfaces = obj.getClass().getInterfaces();
        Object proxy = Proxy.newProxyInstance(classLoader,interfaces,this);
        return proxy;
    }

    /**
     * 通过此JDK动态代理类产生的代理对象,调用方法时都会执行当前invoke方法
     * @param proxy
     * @param method  指代的是通过代理对象调用的方法(insert)
     * @param args  指代的是通过被代理对象调用的方法的参数 (insert方法的参数)
     * @return
     * @throws Throwable
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("=============start");
        Object r = method.invoke(obj, args); //执行insert方法
        System.out.println("=============commit");
        return r;
    }

}

2.测试JDK动态代理

  • 创建接口

    public interface ProductDAO {
        public int insertProduct();
    }
    
  • 创建被代理类(使用JDK动态代理的被代理类要实现接口)

    public class ProductDAOImpl implements ProductDAO {
    
        public int insertProduct() {
            System.out.println("添加商品");
            return 0;
        }
    }
    
  • 测试

    public class TestJDK {
    
        public static void main(String[] args) {
    
            //JDK动态代理只能为实现了接口的类产生代理对象
            ProductDAOImpl productDAO = new ProductDAOImpl();//  被代理对象
    
            //通过JDK动态代理类创建被代理对象的代理对象(代理对象强转成被代理对象实现的接口类型)
            //proxy   代理对象
            ProductDAO proxy = (ProductDAO) new JDKDynamicProxy(productDAO).getProxy(); 
            
    
            //使用代理对象调用方法,并不是直接调用被代理类中方法,
            // 而是执行产生代理对象时指定的InvocationHandler的invoke方法
            // 同时将调用的方法作为参数传递给invoke
            int i = proxy.insertProduct();
        }
    }
    

jdk动态代理拦截机制

通过拦截器拦截insert方法,跳转到invoke()

4.3.2 CGLib动态代理

1.创建CGLib动态代理类

  • 添加CGlib依赖

    <!-- https://mvnrepository.com/artifact/cglib/cglib -->
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.3.0</version>
    </dependency>
    
  • 创建CGLib动态代理类

    /**
     * @Description CGLib动态代理
     * @Author 千锋涛哥
     * 公众号: Java架构栈
     *
     *  CGLib动态代理类创建规则
     *  1.实现MethodInterceptor接口,实现intercept方法
     *  2.在类中定义一个Object类型变量obj,提供有参构造器,用于将被代理对象传递进来
     *  3.定义一个getProxy方法返回代理对象
     *
     */
    public class CGLibDynamicProxy implements MethodInterceptor {
    
        private Object obj;
        public CGLibDynamicProxy(Object obj) {
            this.obj = obj;
        }
    
        public Object getProxy(){
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(obj.getClass());
            //如果使用此动态代理类产生的代理对象调用方法,则回调此类中intercept
            enhancer.setCallback(this);      
            Object proxy = enhancer.create();
            return proxy;
        }
    
    
        /**
         * 使用此动态代理类产生的代理对象调用方法时,并不会执行被调用的方法
         * 而是执行此intercept方法,被调用的方法作为参数传递进来
         * @param o
         * @param method  被调用的方法
         * @param objects 被调用的方法的参数
         * @param methodProxy
         * @return
         * @throws Throwable
         */
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println(">>>>>>>>>>>>>>start");
            Object r	 = method.invoke(obj, objects);
            System.out.println(">>>>>>>>>>>>>>commit");
            return r;
        }
    }
    

2.测试CGLib动态代理

  • 创建被代理类

    public class TypeDAOImpl {
    
        public int insertType(){
            System.out.println("insert type");
            return 0;
        }
    
    }
    
  • 测试

    public class TestCGLib {
    
        public static void main(String[] args) {
            //被代理对象
            TypeDAOImpl typeDAO = new TypeDAOImpl();
            //通过CGLib产生代理对象——代理对象强转成被代理对象类型
            TypeDAOImpl proxy = (TypeDAOImpl) new CGLibDynamicProxy(typeDAO).getProxy();
            //通过代理对象调用方法
            proxy.insertType();
        }
    
    }
    

bject r = method.invoke(obj, objects);
System.out.println(">>>>>>>>>>>>>>commit");
return r;
}
}


**2.测试CGLib动态代理**

- 创建被代理类

```java
public class TypeDAOImpl {

    public int insertType(){
        System.out.println("insert type");
        return 0;
    }

}
  • 测试

    public class TestCGLib {
    
        public static void main(String[] args) {
            //被代理对象
            TypeDAOImpl typeDAO = new TypeDAOImpl();
            //通过CGLib产生代理对象——代理对象强转成被代理对象类型
            TypeDAOImpl proxy = (TypeDAOImpl) new CGLibDynamicProxy(typeDAO).getProxy();
            //通过代理对象调用方法
            proxy.insertType();
        }
    
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值