Spring 5

Spring 工厂创建复杂对象的3种方式

1、FactoryBean接口
  • 实现FactoryBean 接口
    // 返回需要创建的对象
    public Object getObject() throws Exception {
        return null;
    }

    // 返回当前创建对象的Class对象
    public Class<?> getObjectType() {
        return null;
    }


    // 是否单例
    public boolean isSingleton() {
        return false;
    }
  • Spring 配置
<bean id="connection" class="com.lovesosa.spring5.factory.MyFactoryBean"/>

如果配置的bean是 FactoryBean 接口的实现类,那么创建出来的对象是在 FactoryBean getObject() 返回的对象。

  • 如何获取自定义工厂类的对象?
MyFactoryBean factoryBean = (MyFactoryBean) ioc.getBean("&connection");
2、实例工厂

不用实现Spring FactoryBean 接口,无侵入


    <bean id="myFactoryBean" class="com.lovesosa.spring5.factory.MyFactoryBean"/>
    <bean id="conn" factory-bean="myFactoryBean" factory-method="getBean"/>
3、静态工厂

与 实例工厂的差别在于方法是静态方法

<bean id="myFactoryBean" class="com.lovesosa.spring5.factory.MyFactoryBean" factory-method="静态方法名"/>

对象的生命周期

生命周期的三个阶段

  • 创建阶段
  • 初始化阶段
  • 销毁阶段

1.创建阶段

Spring工厂何时创建对象?

  • scope=“singleton”:Spring工厂创建的同时,对象创建
  • scope=“prototype”:Spring工厂会在获取对象的时候创建对象

2.初始化
  • Spring工厂在创建完对象后,调用对象的初始化方法,完成对应的初始化操作。
    • 初始化方法:用户提供,Spring调用

方式一:实现 InitializingBean 接口的 afterPropertiesSet()方法完成初始化操作

方式二:在对象中提供一个普通方法,通过配置文件的init-method="" 指定即可。


3.销毁阶段

Spring在销毁对象前,会调用对象的销毁方法。

  • Sprng什么时候销毁对象?在容器关闭前销毁。ioc.close()
  • 用户自己提供方法,Spring调用

方式一:实现DisposableBean接口 的 destroy()
方式二:与初始化一样,destroy-method=""

注意:销毁方法的操作只适用于 单例Bean。



自定义类型转换器

  • 实现Converter 接口
public class MyConverter implements Converter<String, Date> {

    @Override
    public Date convert(String source) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Date date = null;
        try {
            date = sdf.parse(source);
            return date;
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }
}
  • 注册Converter到 Spring 中
    <bean id="myConverter" class="com.lovesosa.spring5.MyConverter"/>
    <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <ref bean="myConverter"/>
            </set>
        </property>
    </bean>

    <bean id="person" class="com.lovesosa.spring5.bean.Person">
        <property name="name" value="tom"/>
        <property name="age" value="3"/>
        <property name="bir" value="2000-01-02"/>
    </bean>

BeanPostProcessor 后置处理Bean

  • 实现BeanPostProcessor 接口
// 在容器创建好对象后被调用
default Object postProcessBeforeInitialization(Object bean, String beanName);
// 在对象初始化后被调用
default Object postProcessAfterInitialization(Object bean, String beanName);
  • 配置
<bean id="myBeanPostProcessor" class="com.lovesosa.spring5.factory.MyBeanPostProcessor"/>

BeanPostProcessor 会对容器中创建的所有对象进行加工。

Spring的动态代理开发

引入AOP相关依赖

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.1.14.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.3</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.8</version>
        </dependency>
  • 创建目标对象并注入到容器中
  • 实现MethodBeforeAdvice 接口,定义额外功能,并将其注入到Spring容器中
public class Before implements MethodBeforeAdvice {


    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("---MethodBeforeAdvice log---");
    }
}

  • 定义切入点,额外功能加入的位置
    <aop:config>
        <aop:pointcut id="myPointcut" expression="execution(* *(..))"/>
    </aop:config>
  • 组装切入点和额外功能
<aop:advisor advice-ref="before" pointcut-ref="myPointcut"/>
  • 调用,获得Spring创建的动态代理对象,并进行调用(在Spring中,获得原始对象,其实就是代理对象)
        PersonService personService = (PersonService) ioc.getBean("personService");
        personService.login("admin","123");
        personService.register(new Person());

输出

******************************** log ***********************************
register()..............
******************************** log ***********************************
login()..............

Spring创建的动态代理类在哪?

Spring 框架在运行时,通过动态字节码技术,在JVM中创建的,运行在JVM内部,等程序结束后,会和JVM一起消失

1、MethodBeforeAdvice
/**
	method:目标方法
	args:目标方法参数
	target:目标对象
*/
void before(Method method, Object[] args, @Nullable Object target)
2、MethodInterceptor(方法拦截器)

MethodInterceptor接口:额外功能可以根据需要运行在原始方法的前、后、前后

public class Around implements MethodInterceptor {


    /**
     * 额外功能运行在目标方法的前后
     *
     * @param invocation 获得目标方法
     * @return 目标方法的返回值
     * @throws Throwable
     */
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {

        System.out.println("before----------------");
        Object result = invocation.proceed();// 运行目标方法
        System.out.println("after----------------");
        return result;
    }

}

MethodInterceptor可以影响目标方法的返回值。

3、切入点

切入点决定额外功能加入的位置
<aop:pointcut id=“myPointcut” expression=“execution(* *(…))”/>

  • execution():切入点函数
  • * *(..):切入点表达式
    
3.1、切入点表达式
*  *(..):匹配所有方法
*:修饰符和返回值
*:方法名
():参数列表
..:任意参数

注意:在指定参数的时候,如果是非java.lang 包下的类,需要写全限定类名

精准的切点表达式
在这里插入图片描述

忽略包

# 类只存在一级包
* *.类名.*(..)

# 类存在多级包
* *..类名.*(..)

# 切入点包中的所有类,必须在proxy中,不包含子包
* com.lovesosa.proxy.*.*(..)

# 包含子包
* com.lovesosa.proxy..*.*(..)



###### 3.2、切入点函数

切入点函数:用于执行切入点表达式

  • execution

    • 执行 方法 类 包切入点表达式
  • args

    • 作用:主要用于方法参数的匹配,只关注方法参数
    • 例如:args(String,String)
  • within

    • 作用:主要用于进行类、包切入点表达式的匹配
    • 例如:within(*…类名) within(com.lovesosa.proxy… * )
  • @annotation

    • 作用:为具有特殊注解的方法加入额外功能
    • 例如:@annotation(注解)
3.3、切入点函数的逻辑运算

整合多个切入点函数一起配合工作,进而完成更为复杂的需求

  • and与操作,不能用于同种类型的切入点函数
execution(* login(..)) and  args(String,String)
  • or或操作

AOP

1、AOP编程的概念

AOP (Aspect Oriented Programing) 面向切面编程
AOP的概念:本质就是Spring 的动态代理开发,通过代理类为原始类增加额外功能。
好处:利于原始类的维护。

2、AOP编程的开发步骤
  1. 原始对象
  2. 额外功能 (MethodInterceptor)
  3. 切入点
  4. 组装切面(额外功能 + 切入点)
3、切面的概念

切面 = 切入点 + 额外功能

在这里插入图片描述

AOP 底层实现原理
1.核心问题
  • AOP如何创建动态代理类(动态字节码技术)
  • Spring 容器是如何加工创建代理对象的。
2.动态代理类的创建

2.1 JDK的动态代理

Proxy.newInstance()

/**
ClassLoader:通过类加载器创建代理类的Class对象
interfaces:原始对象实现的接口
InvocationHandler:添加额外功能的接口
*/
public class TestJDKProxy {
    public static void main(String[] args) {
        // 1.创建原始对象
        PersonService personService = new PersonServiceImpl();

        // 2.JDK创建动态代理
        Class<? extends PersonService> clazz = personService.getClass();
        PersonService personServiceProxy = (PersonService) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), (p, m, a) -> {
            System.out.println("before");
            Object result = m.invoke(personService, a);
            System.out.println("after");
            return result;
        });


        boolean result = personServiceProxy.login("admin", "admin");
        System.out.println(result);
    }
}

2.2 CGlib的动态代理

  • CGlib创建动态代理的原理:父子继承关系创建代理对象,原始类作为父类,代理类作为子类,这样即保证2者方法一致,同时在代理类中提供新的实现(额外功能+原始方法)

        // 1.创建原始对象
        UserService userService = new UserService();

        // 2.通过CGlib的方式创建动态代理对象
        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(TestCGlib.class.getClassLoader());
        enhancer.setSuperclass(userService.getClass());
        MethodInterceptor interceptor = (o, m, a, p) -> {
            System.out.println("***********cglib日志");
            return m.invoke(userService,a);
        };

        enhancer.setCallback(interceptor);

        UserService userServiceProxy = (UserService) enhancer.create();
        userServiceProxy.login("admin", "123456");

对比两者创建动态代理的方式

  • JDK动态代理:Proxy.newProxyInstance() 通过接口创建代理的实现类
  • CGlib动态代理:Enhancer 通过继承父类创建代理类
3.在看BenaPostProcessor

在这里插入图片描述

public class ProxyBeanPostProcessor implements BeanPostProcessor {


    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return Proxy.newProxyInstance(ProxyBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), (p, m, a) -> {
            System.out.println("--- log ---");
            return m.invoke(bean,a);
        });
    }
}

<bean id="userService" class="com.lovesosa.spring5.factory.UserServiceImpl"/>
<bean id="postProcessor" class="com.lovesosa.spring5.factory.ProxyBeanPostProcessor"/>
4. 基于注解的AOP编程

定义切面类

@Aspect
public class MyAspect {


    @Around(value = "execution(* com.lovesosa.spring5.aspect..UserServiceImpl.*(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {

        System.out.println("前置通知");

        Object ret = joinPoint.proceed();


        return ret;
    }

}

    <bean id="userService" class="com.lovesosa.spring5.aspect.UserServiceImpl"/>

    <bean id="around" class="com.lovesosa.spring5.aspect.MyAspect"/>

    <aop:aspectj-autoproxy/>

切入点复用

    @Pointcut("execution(* com.lovesosa.spring5.aspect..UserServiceImpl.*(..))")
    public void myPointcut() {}


    @Around("myPointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("前置通知");

        Object ret = joinPoint.proceed();
        return ret;
    }

Spring对于JDK 与 CGlib 的切换
默认情况下,AOP编程底层用的是JDK动态代理的方式,如何切换CGlib?
针对基于注解的
<aop:aspectj-autoproxy proxy-target-class=“true”/>

针对传统的方式


    <aop:config proxy-target-class="true">
        <aop:pointcut id="myPointcut" expression="execution(* login(..))"/>


        <!--组装额外功能和切入点-->
        <aop:advisor advice-ref="around" pointcut-ref="myPointcut"/>
    </aop:config>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值