程序员之家

2020-03-16 Spring-Aop

2020-03-16  本文已影响0人  summer96

简介

AOP:面向切面编程,是OOP的扩展和延申,解决OOP中遇到的问题
可以进行权限校验,日志记录,性能监控,事务校验
Spring底层的AOP实现原理:动态代理
JDK动态代理 :只能对实现了接口的类产生代理
Cglib动态代理(类似于Javassist第三方代理技术):对没有实现接口的类产生代理对象,生成子
对象

两种动态代理

JDK动态代理实例

1.建立一个接口:

public interface UserDao {
    public void insert();
    public void update();
}

2.接口实现类

public class UserDaoImpl implements UserDao {
    @Override
    public void insert() {
        System.out.println("insert方法");
    }
    @Override
    public void update() {
        System.out.println("update方法");
    }
}

3.编写代理类JdkProxy

public class JdkProxy{
    //将被增强的对象传递到代理当中
    private UserDao userDao;
    public JdkProxy(UserDao userDao) {
        this.userDao = userDao;
    }
    public UserDao creatProxy() {
        UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(), userDao.getClass().getInterfaces(), this);
        return userDaoProxy;
    }
}
  1. 实现InvocationHandler接口,并重写其中方法,用于权限管理
public class JdkProxy implements InvocationHandler{
        ······
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if("insert".equals(method.getName())) {
            System.out.println("权限增强");
            return method.invoke(userDao, args);
        }
        return method.invoke(userDao, args);
    }

5.测试类

    @Test
    public void demo1() {
        UserDao userDao=new UserDaoImpl();
        //创建代理
        UserDao proxy = new JdkProxy(userDao).creatProxy();
        proxy.insert();
        proxy.update();
    }

Cglib动态代理

第三方开源代码生成类库,动态添加类的属性和方法
1.编写接口和测试类(同上)
2.编写代理类CglibProxy

public class CglibProxy {
    private UserDaoImpl userDaoImpl;
    public CglibProxy(UserDaoImpl userDaoImpl) {
        this.userDaoImpl=userDaoImpl;
    }
    public UserDaoImpl createProxy() {
        //1.创建Cglib的代理对象
        Enhancer enhancer = new Enhancer();
        //2.设置父类
        enhancer.setSuperclass(userDaoImpl.getClass());
        //3.设置回调
        enhancer.setCallback(this);
        //4.创建代理
        UserDaoImpl proxy=(UserDaoImpl) enhancer.create();
        return  proxy;
    }

3.CglibProxy 继承MethodInterceptor类,并重写其中方法

public class CglibProxy implements MethodInterceptor{
    ······
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        // 判断是否是insert方法
        if("insert".equals(method.getName())) {
            //增强
            System.out.println("权限校验");
            return methodProxy.invokeSuper(proxy, args);
        }
        return methodProxy.invokeSuper(proxy, args);
    }
}

4.测试类

    @Test
    public void demo1() {
        UserDaoImpl userDao=new UserDaoImpl();
        //创建代理
        UserDaoImpl proxy = new CglibProxy(userDao).createProxy();
        proxy.insert();
        proxy.update();
    }

Spring的AOP开发

简介

AOP思想最早是由AOP联盟组织提出的,Spring是使用这种思想最好的框架
Spring的AOP有自己实现的方式(非常繁琐)AspectJ是一个AOP框架,Spring引入了AspectJ作为自身开发

相关代码编写

1.新建切面类,并在切面类中MethodInterceptor类(环绕通知)

//环绕通知
public class MyAspectXML implements MethodInterceptor{
    @Override
    public Object invoke(MethodInvocation arg0) throws Throwable {
        System.out.println("0000000000");
        Object obj=arg0.proceed();
        System.out.println("0000000000");
        return obj;
    }
 }

2.在xml文件中配置

<!-- 配置目标对象,被增强的对象-->
<bean id="productDao" class="com.zut.aopTest.ProductDaoImpl"></bean>
<!-- 将切面类交给Spring管理 -->
<bean id="myAspect" class="com.zut.aopTest.MyAspectXML"></bean>

3.在xml文件中配置通知

<!-- 创建代理对象  默认JDK动态代理  如果没有proxyInterfaces 则默认cglib-->
    <bean class="org.springframework.aop.framework.ProxyFactoryBean" name="proxyBean">
        <property name="targetName" value="productDao"></property>
        <!-- 拦截器名字 -->
        <property name="interceptorNames" value="myAspect">
            <!-- 如果通知在多个类中 -->
            <!-- <array>
                <value></value>
            </array> -->
        </property>
            <property name="proxyInterfaces" value="com.zut.aopTest.ProductDao"></property>
    </bean>

4.编写测试类

public void demo1() {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        ProductDao productDao=context.getBean("proxyBean",ProductDao.class);
        productDao.save();
}

前置通知接口:MethodBrforeAdvice
后置通知接口:AfterReturningAdvice 注意: 有异常时后置通知不执行
异常通知接口:ThrowsAdvice
最终通知接口:AfterAdvice

在此方式下,xml文件可以采用自动织入编写

1.xml文件

<!-- 配置目标对象,被增强的对象 -->
    <bean id="productDao" class="com.zut.aopTest.ProductDaoImpl"></bean>
    <!-- 将切面类交给Spring管理 -->
    <bean id="myAspect" class="com.zut.aopTest.MyAspectXML"></bean>
<aop:config>
        <aop:pointcut expression="execution(* com.zut.springAOP.UserImpl.*(..))" id="pointcut"/>
        <aop:advisor advice-ref="myAspect" pointcut-ref="pointcut"/>
    </aop:config>
 //id 与 pointcut-ref 相等

2.测试类

public void demo1() {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        ProductDao productDao=context.getBean("productDao",ProductDao.class);
        productDao.save();
}

采用AspectJ的XML的方式

相关配置文件

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.2.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>aopalliance</groupId>
    <artifactId>aopalliance</artifactId>
    <version>1.0</version>
</dependency>
<dependency>
    <groupId>aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.5.3</version>
</dependency>

xml文件

clipboard.png

相关代码编写

1.编写接口和测试类
2.编写切面类

public class MyAspectXML {
    public void checkPri() {
        System.out.println("权限校验。。。。。。。。。");
    }
}

3.在xml文件里进行配置

<!-- 配置目标对象,被增强的对象 -->
    <bean id="productDao" class="com.aopTest.ProductDaoImpl"></bean>
    <!-- 将切面类交给Spring管理 -->
    <bean id="myAspect" class="com.aopTest.MyAspectXML"></bean>
    <!-- 通过AOP的配置实现对目标产生代理 -->
    <aop:config>
        <!-- 表达式配置那些类哪些方法需要进行增强   ()里面只写参数类型  可以写成 save(int ,String) and args(a,b)  -->
        <aop:pointcut expression="execution(* com.aopTest.ProductDaoImpl.save(..))" id="pointcut1"/>
        <!-- 配置切面  myAspect指向切面 与上文要对应上  checkPri 切面类里的方法名-->
        <aop:aspect ref="myAspect">
                      <!--匹配参数  这里的名字要一致-->
            <aop:before method="checkPri" pointcut-ref="pointcut1" arg-names="a"/></aop:aspect>   
    </aop:config>
</beans>

4.测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:Spring-config.xml")
public class SpringDemo1 {
    @Resource(name="productDao")
    private ProductDao productDao;
    @Test
    public void demo1() {
        productDao.save();
        productDao.update();
        productDao.find();
        productDao.delete();
    }
}

通知类型

前置通知

在目标方法执行之前执行此操作
1.xml文件

<!-- 配置切面 -->
<aop:aspect ref="myAspect">
<aop:before method="checkPri" pointcut-ref="pointcut1"/>
</aop:aspect>

2.获得切入点信息(几种通知均可实现)
在切入类中

public void checkPri(JoinPoint joinPoint) {
        System.out.println("权限校验。。。。。。。。。"+joinPoint);
}

后置通知

在目标方法之后执行的操作
1.在xml文件中

<aop:pointcut expression="execution(* com.aopTest.ProductDaoImpl.delete(..))" id="pointcut2"/>

<!-- 后置通知 -->
<aop:after-returning method="write" pointcut-ref="pointcut2" returning="result"/>
//returning表示获取返回值

2.获得方法返回值
在切入类中

/**
 * 后置通知
 */
    public void write(Object result) {
        System.out.println("日志通知。。。。。。。。。。"+result);
    }
 //这里的reult必须与前面配置里的returning一致

环绕通知

在目标方法执行之前和之后进行操作
1.xml文件

<aop:pointcut expression="execution(* com.aopTest.ProductDaoImpl.update(..))" id="pointcut3"/>

<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pointcut3"/>

2.在切入类

/**
 * 环绕通知
 * 性能监控
 * @throws Throwable 
 */
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
    System.out.println("环绕通知前。。。");
    Object obj=joinPoint.proceed();
    System.out.println("环绕通知后。。。");
    return obj;
}

异常抛出通知

在程序出现异常时,进行的操作
1.xml文件中

<aop:pointcut expression="execution(* com.aopTest.ProductDaoImpl.find(..))" id="pointcut4"/>

<!-- 异常通知 -->
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="ex"/>
//throwing:得到异常类型

2.切入类

/**
 * 异常抛出
 */
public void afterThrowing(Throwable ex) {
    System.out.println("异常抛出。。。"+ex);
}
 //ex必须与上文配置中的throwing一致

最终通知

无论代码是否有异常,总是会执行,相当于finally代码块
1.xml文件里

<!-- 最终通知 -->
<aop:after method="after" pointcut-ref="pointcut4"/>

2.切面类

/**
* 最终通知
*/
public void after() {
    System.out.println("最终通知。。。...");
}

spring的切入点表达式写法

基于execution的函数完成的
语法:
[访问修饰符] 方法返回值 包名.类名.方法名(参数)
public void com.zut.aopTest.ProductDao.save(..)
星号(*)代表任意,参数不能写 * ,可以写 ..
*****Dao.save(..)
*com.zut.aopTest.ProductDao+save(..)

上一篇下一篇

猜你喜欢

热点阅读