Spring AOP

JAVA代理

代理模式(Proxy)是通过代理对象访问目标对象,这样可以在目标
对象基础上增强额外的功能,如添加权限,访问控制和审计等功能

代理对象:
1、代理对象存在的价值主要用于拦截对真实业务对象的访问。
2、代理对象应该具有和目标对象(真实业务对象)相同的方法。

代理分类:
1、静态代理
2、动态代理(jdk动态代理和Cglib动态代理)

静态代理实现原理

静态代理实现原理.png

实现
静态代理实现.png

springAOP实现方式

1.动态代理 使用 Proxy机制,使用反射技术,创建实际对象的代理对象,添加AOP的逻辑后执行。
2.动态字节码增强 ASM CGLIB的工具库实现

aop实现⽅式1:

interface接口

public interface IUserService<T> {

    /**
     * 获取所有用户列表
     * @return
     */
    List<T> getAllUser();

    /**
     * 保存用户
     * @param user
     * @return
     */
    boolean saveUser(T user);

    /**
     * 根据uid删除用户
     * @param uid
     * @return
     */
    boolean deleteUser(int uid);

    /**
     * 更新用户
     * @param obj
     * @return
     */
    boolean updateUser(T obj);
}

implements

public class UserServiceImpl implements IUserService<Object>{
    public List<Object> getAllUser() {
        System.out.println("-------getAllUser----------");
        return new ArrayList<Object>();
    }

    public boolean saveUser(Object user) {
        System.out.println("-------saveUser----------");
        return true;
    }

    public boolean deleteUser(int uid) {
        System.out.println("-------deleteUser----------");
        return true;
    }

    public boolean updateUser(Object obj) {
        System.out.println("-------updateUser----------");
        return true;
    }

Aspect

public class MyAspect {
    public void before(){
        System.out.println("-----------before---------------");
    }
    public void after(){
        System.out.println("-----------after---------------");
    }
}

factory

public class UserFactory {
    public static IUserService getUserService(){
        final IUserService userService=new UserServiceImpl();
        final MyAspect ma=new MyAspect();
        IUserService ius= (IUserService) Proxy.newProxyInstance(UserFactory.class.getClassLoader(), userService.getClass().getInterfaces(), new InvocationHandler() {
            /**
             * 代理对象调用的回调方法
             * @param proxy 代理对象
             * @param method 被代理的方法
             * @param args 被代理的方法的参数列表对象
             * @return 每个方法的最终返回值
             * @throws Throwable
             */
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                ma.before();
                System.out.println("method:"+method);
                System.out.println("args:"+args);
                Object obj = method.invoke(userService,args);
                ma.after();
                System.out.println("obj:"+obj);
                return obj;
            }
        });
        return ius;
    }

AOP实现方式2

public class UserFactory {

    /**
     * 使用spring中的一个增强类实现aop方式
     *      1、创建Enhancer对象
     *      2、设置增强类Enhancer的superClass
     *      3、设置Enhancer对象的回调
     *      4、通过eh对象的create()方法来指定对象
     * @return
     */
    public static IUserService getUserService(){
        final MyAspect ma = new MyAspect();
        final IUserService<Object> userService=new UserServiceImpl();
        //1、创建Enhancer对象
        Enhancer eh=new Enhancer();
        //2、设置增强类Enhancer的superClass
        eh.setSuperclass(IUserService.class);
        //3、设置Enhancer对象的回调
        eh.setCallback(new MethodInterceptor() {
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                ma.before();
                System.out.println("method:"+method);
                //System.out.println("objects:"+objects);
                Object obj=method.invoke(userService,objects);
                ma.after();
                System.out.println("obj:"+obj);
                return obj;
            }
        });
        //4、通过eh对象的create()方法来指定对象
        IUserService<Object> iUserService= (IUserService<Object>) eh.create();
        return iUserService;
    }

AOP实现方式3


    <bean id="us" class="com.qfedu.aop3.UserServiceImpl"/>
    <bean id="my" class="com.qfedu.aop3.MyAspect"/>
    <!--
        ProxyFactoryBean代理的FactoryBean对象,我们现在要代理的是us
            包含四个属性注入:
                1.  interfaces: 接口对象们
                    <list>
                        <value>com.qfedu.aop03.IUserService</value>
                        <value>com.qfedu.aop03.IUserService</value>
                        <value>com.qfedu.aop03.IUserService</value>
                    </list>
                2.  target:目标对象,哪个对象将被以代理的方式创建
                3.  interceptorNames:拦截对象的名称,自定义的MethodInterceptor对象,注意它的包结构组成
                4.  optimize:boolean类型的值:
                        true:强制使用cglib的动态代理方式
                        false:使用jdk自带的动态代理

                        cglib:code generation library,代码生成库,性能更高

    -->
    <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="interfaces" value="com.qfedu.aop3.IUserService"/>
        <property name="target" ref="us"/>
        <property name="interceptorNames" value="my"/>
        <property name="optimize" value="true"/>
    </bean>

MyAscept

public void before(){
        System.out.println("-----------before---------------");
    }
    public void after(){
        System.out.println("-----------after---------------");
    }

    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        before();
        //处理业务方法的调用
        Object obj = methodInvocation.proceed();
        after();
        return obj;
    }

Test类

public class TestUser {
    @Test
    public void testUser(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("applications.xml");
        IUserService us=ac.getBean("proxy",IUserService.class);
        System.out.println(us.getAllUser());
        System.out.println(us.saveUser(new Object()));
        System.out.println(us.deleteUser(1));
        System.out.println(us.updateUser(new Object()));
    }
}

简单AOP实现

Spring 方面可以使用下面提到的五种通知工作:


5种通知.png

先添加aspectj依赖

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

AOP实现方式4

<bean id="us" class="com.qfedu.aop4.UserServiceImpl"/>
    <bean id="ma" class="com.qfedu.aop4.MyAspect"/>
    <!--
        proxy-target-class设置为true,强制使用cglib方式来实现动态代理
    -->
    <aop:config proxy-target-class="true">
        <!--
            定义一个aop的切点
            该包com.qfedu.aop04下所有的任意类,类下的任意含参不含参数的方法,返回值必须为List的方法将会被代理
            <aop:pointcut id="pt" expression="execution(java.util.List com.qfedu.aop04.*.*(..))" />
            该包com.qfedu.aop04下所有的任意类,类下的任意含参不含参数的方法,返回值必须为boolean的方法将会被代理
            <aop:pointcut id="pt" expression="execution(boolean com.qfedu.aop04.*.*(..))" />

            com.qfedu.aop04.UserServiceImpl.deleteUser(int)具体的方法了,参数为int,返回值任意
            <aop:pointcut id="pt" expression="execution(* com.qfedu.aop04.UserServiceImpl.deleteUser(int))" />
        -->
        <aop:pointcut id="pt" expression="execution(* com.qfedu.aop4.*.*(..))"/>
        <!--
            通知,将MyAspect与切点关联起来
        -->
        <aop:advisor advice-ref="ma" pointcut-ref="pt"/>
    </aop:config>

AOP实现方式5

MyAspect

public class MyAspect {
/**
 * 五种通知方式来实现aop
 *  1. 前置通知,在业务方法之前执行
 *  2. 后置通知,在业务方法之后执行
 *  3. 环绕通知,同时在业务方法的前后执行
 *  4. 带有返回值通知,可以拿到业务方法的返回值
 *  5. 异常通知,可以捕获业务方法中的异常对象
 *
 *      注意:如果同时配置来所有的通知方式,则执行顺序依次为:
 *          before>around before>业务方法 >after returning > around after > after
 *          before>around before>业务方法 >after throwing >  after
 *
 *          ps. 使用注解的话是环绕通知proceed方法之前部分先执行,使用xml配置的话取决于aop:before和aop:around的配置顺序
 */
    public void myBefore(JoinPoint jp){
        System.out.println("this is myBefore");
    }
    public void myAfter(JoinPoint jp){
        System.out.println("this is myAfter");
    }
    public Object myAround(ProceedingJoinPoint pjp){
        try {
            System.out.println("this is myAroundBefore");
            Object obj = pjp.proceed();
            System.out.println("this is myAroundAfter:"+obj);
            return obj;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }
    public void myReturn(JoinPoint jp,Object obj){
        System.out.println("this is returning:"+obj);
    }
    public void myThrowing(JoinPoint jp,Throwable e){
        System.out.println("this is throwing:"+e.getMessage());
    }
}

<bean id="us" class="com.qfedu.aop5.UserServiceImpl"/>
    <bean id="ma" class="com.qfedu.aop5.MyAspect"/>
    <aop:config expose-proxy="true">
        <aop:aspect ref="ma">
            <aop:pointcut id="mpc" expression="execution(* com.qfedu.aop5.*.*(..))"/>
            <aop:before method="myBefore" pointcut-ref="mpc"/>
            <aop:after method="myAfter" pointcut-ref="mpc"/>
            <aop:around method="myAround" pointcut-ref="mpc"/><!--环绕通知-->
            <aop:after-returning method="myReturn" pointcut-ref="mpc" returning="obj"/>
            <aop:after-throwing method="myThrowing" pointcut-ref="mpc" throwing="e"/>
        </aop:aspect>
    </aop:config>

AOP实现方式6

<!--
    context:component-scan 组件扫描
        base-package指定要扫描的包的路径
    -->
    <context:component-scan base-package="com.qfedu.aop6"/>
    <!--aop:aspectj-autoproxy标签实现自动代理-->
    <aop:aspectj-autoproxy/>

要在实现类中加入注解@Component并命名

@Component("us")
public class UserServiceImpl implements IUserService {
    public List<Object> getAllUser() {
        System.out.println("----------getAllUser-------------");
        return new ArrayList<Object>();
    }

    public boolean saveUser(Object user) {
        System.out.println("----------saveUser-------------");
        return true;
    }

    public boolean deleteUser(int uid) {
        System.out.println("----------deleteUser-------------");
        return true;
    }

    public boolean updateUser(Object obj) {
        System.out.println("----------updateUser-------------");
        return false;
    }
}

在MyAspect中加入注解

/**
 *  以注解的方式实现的切面类MyAspect
 *
 *      当前类中的五种通知方式均以注解方式完成
 */
@Component         //  标注当前类为一个组件
@Aspect                //  标注当前类为一个切面类
public class MyAspect {
/**
     * @Pointcut 注解为了避免相同的匹配规则被定义多处,专门定义该方法设置执行的匹配规则,各个自行调用即可
     *    write once, only once
     */
    @Pointcut(value = "execution(* com.qfedu.aop6.*.*(..))")
    public void setAll(){}
/**
     * @Before 表示该方法为一个前置通知
     * @param jp 连接点
     */
    @Before("setAll()")
    public void myBefore(JoinPoint jp){
        System.out.println("this is myBefore");
    }
/**
     * @After 表示该方法为一个后置通知
     * @param jp 连接点
     */
    @After("setAll()")
    public void myAfter(JoinPoint jp){
        System.out.println("this is myAfter");
    }
/**
     * @Around 表示该方法为一个环绕通知
     * @param pjp 处理连接点
     * @return 返回每个业务方法的返回值
     */
    @Around("setAll()")
    public Object myAround(ProceedingJoinPoint pjp){
        try {
            System.out.println("this is myAroundBefore");
            Object obj = pjp.proceed();
            System.out.println("this is myAroundAfter:"+obj);
            return obj;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }
/**
     * @AfterReturning 表示该方法为一个带有返回值的通知
     * @param jp 连接点
     * @param obj 业务方法的返回值
     */
    @AfterReturning(value = "setAll()",returning = "obj")
    public void myReturn(JoinPoint jp,Object obj){
        System.out.println("this is returning:"+obj);
    }
/**
     * @AfterThrowing 表示该方法为一个带有异常的通知
     * @param jp 连接点
     * @param e Throwable对象
     */
    @AfterThrowing(value = "setAll()",throwing = "e")
    public void myThrowing(JoinPoint jp,Throwable e){
        System.out.println("this is throwing:"+e.getMessage());
    }
}

Test

@Test
    public void testUser(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("beans3.xml");
//此处的us就是实现类中加入注解@Component并命名的us
        IUserService us = ac.getBean("us", IUserService.class);
        us.getAllUser();
        us.deleteUser(1);
        us.saveUser("ysx");
        us.updateUser("yanshixian");
    }

AOP实现方式7(使用BeanPostProcessor方式实现Spring的AOP)

<!--
        context:component-scan上下文的组件扫描
            base-package指定要扫描的包为com.qfedu.aop07
    -->
    <context:component-scan base-package="com.qfedu.aop7"/>
    <!--
       指定BeanPostProcessor的Factory hook,让每个bean对象初始化是自动回调该对象中的回调方法
    -->
    <bean class="com.qfedu.aop7.MyBeanPostProcessor"/>

MyBeanPostProcessor

**
 * 配置了包扫描之后,该类会初始化两个对象EventListenerMethodProcessor和DefaultEventListenerFactory,再外加我们自己的组件对象
 *
 * 所以会发现有三个before打印
 *
 * 我还专门加了一个UserServiceImpl2,你会发现将有四个before的打印(注意看看该类上的Component注解是否启用)
 *
 */
public class MyBeanPostProcessor implements BeanPostProcessor {
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        //System.out.println("this is before");
        return bean;
    }

    public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
        //System.out.println("this is after");
        return Proxy.newProxyInstance(MyBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                MyAspect ma = new MyAspect();
                ma.before();
                Object obj = method.invoke(bean, args);
                ma.after();
                return obj;
            }
        });
    }
}

UserServiceImpl

@Component("us")
public class UserServiceImpl implements IUserService {
    public List<Object> getAllUser() {
        System.out.println("----------getAllUser-------------");
        return new ArrayList<Object>();
    }

    public boolean saveUser(Object user) {
        System.out.println("----------saveUser-------------");
        return true;
    }

    public boolean deleteUser(int uid) {
        System.out.println("----------deleteUser-------------");
        return true;
    }

    public boolean updateUser(Object obj) {
        System.out.println("----------updateUser-------------");
        return false;
    }
}

BeanFactory 与 AppliacationContext 有什么区别

  1. BeanFactory
    基础类型的 IOC 容器,提供完成的 IOC 服务支持。如果没有特殊指定,默认采用延迟初始化策略。相对来说,容
    器启动初期速度较快,所需资源有限。
    2.ApplicationContext
    ApplicationContext 是在 BeanFactory 的基础上构建,是相对比较高级的容器实现,除了 BeanFactory 的所有
    支持外,ApplicationContext 还提供了事件发布、国际化支持等功能。ApplicationContext 管理的对象,在容器启动
    后默认全部初始化并且绑定完成。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。