2020-03-16 Spring-Aop

简介

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(..)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容