【Java EE 学习 51】【Spring学习第三天】【cglib动态代理】【AOP和动态代理】【切入点表达式】
一、cglib动态代理
1.简介
(1)CGlib是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
(2) 用CGlib生成代理类是目标类的子类。
(3)用CGlib生成 代理类不需要接口
(4)用CGLib生成的代理类重写了父类的各个方法。
(5)拦截器中的intercept方法内容正好就是代理类中的方法体
2.Spring什么时候使用JDK动态代理,什么时候使用CGLib动态代理?
(1)若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。
优点:因为有接口,所以使系统更加松耦合
缺点:为每一个目标类创建接口
(2)若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。
优点:因为代理类与目标类是继承关系,所以不需要有接口的存在。
缺点:因为没有使用接口,所以系统的耦合性没有使用JDK的动态代理好。
3.不使用Spring,使用CGlib动态代理示例。
package com.kdyzm.spring.proxy; public class Person {
private String name; public Person(String name) {
this.name = name;
} public Person() {
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} @Override
public String toString() {
return "Person [name=" + name + "]";
}
}
com.kdyzm.spring.proxy.Person
package com.kdyzm.spring.proxy;
/*
* 这里测试的是cplib动态代理,不需要实现某个接口
*/
public class PersonDaoImpl{ public void savePerson() {
System.out.println("保存学生!");
} public Person getPerson() {
Person p=new Person();
p.setName("狗蛋");
return p;
} public void updatePerson() {
System.out.println("更新学生信息!");
} public void deletePerson() {
System.out.println("删除学生!");
} }
com.kdyzm.spring.proxy.PersonDaoImpl
这里直接使用PersonDaoImpl类,因为使用CGLib动态代理目标类不需要有接口,代理对象是目标类的子类实例。
package com.kdyzm.spring.proxy; public class Transaction {
public void startTransaction(){
System.out.println("开启事务!");
}
public void commit(){
System.out.println("提交事务!");
}
}
com.kdyzm.spring.proxy.Transaction
最重要的一个类是
com.kdyzm.spring.proxy.PersonDaoInterceptor,该类是实现CGLib动态代理的关键。
package com.kdyzm.spring.proxy; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy; public class PersonDaoInterceptor implements MethodInterceptor{
private Object target;
private Transaction transaction; public PersonDaoInterceptor(Object target, Transaction transaction) {
this.target = target;
this.transaction = transaction;
} //定义一个获取代理对象的方法
public Object createProxyObject(){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
enhancer.setCallback(this);//设置拦截器为回调函数
return enhancer.create();//创建代理对象
} @Override
public Object intercept(Object arg0, Method method, Object[] args,
MethodProxy arg3) throws Throwable {
Object obj=null;
String methodName=method.getName();
if("savePerson".equals(methodName)||"updatePerson".equals(methodName)||"deletePerson".equals(methodName)){
this.transaction.startTransaction();
obj=method.invoke(this.target, args);
this.transaction.commit();
}
else{
System.out.println("方法名为:"+method.getName()+",不拦截!");
obj=method.invoke(this.target, args);
}
return obj;
} }
最后测试类:com.kdyzm.spring.proxy.PersonDaoInterceptorTest
package com.kdyzm.spring.proxy; import org.junit.Test; public class PersonDaoInterceptorTest {
@Test
public void testOne(){
Transaction transaction = new Transaction();
PersonDaoImpl target = new PersonDaoImpl();
PersonDaoInterceptor personDaoInterceptor = new PersonDaoInterceptor(target, transaction);
PersonDaoImpl proxy=(PersonDaoImpl) personDaoInterceptor.createProxyObject();
Person person=proxy.getPerson();
System.out.println(person);
proxy.updatePerson();
}
}
运行结果:
方法名为:getPerson,不拦截!
Person [name=狗蛋]
开启事务!
更新学生信息!
提交事务!
4.使用CGLib动态代理需要注意的事项:
如果不在spring中使用,则只需要导入cglib相关的jar包即可。
二、传统的动态代理实现的缺陷
不管采用JDK动态代理生成代理类还是采用CGLIB生成动态代理类。目标类中的所有方法都被拦截下来。而在哪个方法里做比如权限的判断、安全性的检查等一系列工做必须在拦截器中作相应的判断。但是这样的编程形式给程序的编写带来了一定的麻烦。
1. 在拦截器中控制哪些方法将被做权限判断、安全性检查等是一件比较困难的事情。
(1)采取这样的配置目标类只能是一个,所以如果用这种方法做权限控制,得写很多代理,这样给代码的书写造成了困难。
(2)每一个类中的每一个方法如果都有不同的权限(实际的系统往往都是这样的),在拦截器中的判断代码书写会很困难。
2.这样的代码也会导致硬编码,也就是说我们必须在拦截器中写一些权限判断等事情,会导致拦截器中代码量的增大,造成维护的麻烦。
三、AOP编程
1.几个概念
(1)Aspect(切面)
比如说事物、权限等等,和业务逻辑没有关系的部分。
(2)joinpoint(连接点)
目标类的目标方法,由客户端调用的时候决定。
(3)Pointcut(切入点)
所谓切入点是指我们要对哪些拦截的方法的定义。
被纳入spring aop中的目标类的方法。
(4)Advice(通知)
所谓通知是指拦截到joinpoint之后要做的事情。通知分为前置通知、后置通知、异常通知、最终通知、环绕通知(切面要完成的功能(方法))
(5)Target(目标对象)
代理的目标对象
(6)Weaving(织入)
是指把切面应用到目标对象来创建新的代理对象的过程。切面在指定的连接点织入到目标对象。
2.第一个小案例:
首先需要引入lib/aspectj文件夹中的两个jar包。
package com.kdyzm.spring.proxy.xml; public class Person {
private String name; public Person(String name) {
this.name = name;
} public Person() {
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} @Override
public String toString() {
return "Person [name=" + name + "]";
}
}
com.kdyzm.spring.proxy.xml.Person
package com.kdyzm.spring.proxy.xml;
/*
* 这里测试的是cplib动态代理,不需要实现某个接口
*/
public class PersonDaoImpl{ public void savePerson() {
System.out.println("保存学生!");
} public Person getPerson() {
Person p=new Person();
p.setName("狗蛋");
return p;
} public void updatePerson() {
System.out.println("更新学生信息!");
} public void deletePerson() {
System.out.println("删除学生!");
} }
com.kdyzm.spring.proxy.xml.PersonDaoImpl
package com.kdyzm.spring.proxy.xml; public class Transaction {
public void startTransaction(){
System.out.println("开启事务!");
}
public void commit(){
System.out.println("提交事务!");
}
}
com.kdyzm.spring.proxy.xml.Transaction
最重要的是配置文件,这里不需要明确使用哪种代理方式,因为没有给目标类加上要实现的接口,所以spring会自动使用CGLib动态代理方式,通过debug模式可以看出来。
<?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"
xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
">
<bean id="personDao" class="com.kdyzm.spring.proxy.xml.PersonDaoImpl"></bean>
<bean id="transaction" class="com.kdyzm.spring.proxy.xml.Transaction"></bean> <aop:config>
<!-- 切入点表达式确定哪个类能够生成代理对象 -->
<aop:pointcut expression="execution(* com.kdyzm.spring.proxy.xml.PersonDaoImpl.*(..))" id="aimClass"/>
<!-- 切面 -->
<aop:aspect ref="transaction">
<aop:before method="startTransaction" pointcut-ref="aimClass"/>
<aop:after method="commit" pointcut-ref="aimClass"/>
</aop:aspect>
</aop:config>
</beans>
可以发现配置文件的命名空间多了一个,而且需要另外的一个schema约束。
xmlns:aop="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
测试类:com.kdyzm.spring.proxy.xml.AOPTest
package com.kdyzm.spring.proxy.xml; import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; public class AOPTest {
@Test
public void testOne(){
ApplicationContext context=new ClassPathXmlApplicationContext("com/kdyzm/spring/proxy/xml/applicationContext.xml");
PersonDaoImpl personDaoImpl = (PersonDaoImpl) context.getBean("personDao");
personDaoImpl.deletePerson();
}
}
运行结果:
开启事务!
删除学生!
提交事务!
使用的代理方式一定是CGLib:
3.各种通知
(1)前置通知
形式
<aop:before method="startTransaction" pointcut-ref="aimClass"/>
在切面Transaction中的对应方法中,可以加上JoinPoint参数,通过该参数能够获取目标类的目标方法名称、参数值等信息。
public void startTransaction(JoinPoint joinPoint){
System.out.println("开始事务!");
}
(2)后置通知
<aop:after-returning method="commit" pointcut-ref="aimClass" returning="val"/>
目标方法执行完成之后执行该通知,通知中可以带有两个参数,JoinPoint joinPoint和Object val。
joinPoint:通过该对象能够获取目标类和目标方法的一切信息,包括目标方法的参数列表。
val:通过该对象能够获取目标方法执行之后的返回值,如果没有返回值,则val为null;val一定要和方法中的参数名称完全相同。
注意:如果在执行目标类的目标方法中遇到异常,则不执行后置通知。
(3)异常通知
<aop:after-throwing method="exceptionMethod" pointcut-ref="aimClass" throwing="ex"/>
切面中的通知可以带有两个参数:JoinPoint joinPoint,Throwalbe ex
ex一定要与throwing="ex"中的相同。
(4)环绕通知
<aop:around method="aroundTest" pointcut-ref="aimClass"/>
环绕通知的作用就是拦截目标方法的执行,可以在执行之前和执行之后做一些动作。
切面中的通知可以有一个参数:ProceedingJoinPoint joinPoint,该接口是JoinPoint的子接口,所以通过该接口的引用仍然能够获取想要获取到的有关目标类和目标方法的所有信息。另外,该类提供了procced方法,用于执行目标方法。
public void aroundTest(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("拦截方法的执行!");
joinPoint.proceed();
}
(5)最终通知
<aop:after method="finallyMethod" pointcut-ref="aimClass"/>
最终通知大多数用于释放在执行目标方法的时候占用的资源,即使产生了异常,后置通知不被执行,但是最终通知一定会被执行。
也就是说在最终通知中不受异常的影响。也就是说不论目标方法执行的过程中是否抛出异常,最终通知都将执行。
四、切入点表达式
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)
throws-pattern?)
除了返回类型模式(上面代码片断中的ret-type-pattern),名字模式和参数模式以外, 所有的部分都是可选的。返回类型模式决定了方法的返回类型必须依次匹配一个连接点。 你会使用的最频繁的返回类型模式是*
,它代表了匹配任意的返回类型。 一个全限定的类型名将只会匹配返回给定类型的方法。名字模式匹配的是方法名。 你可以使用*
通配符作为所有或者部分命名模式。 参数模式稍微有点复杂:()
匹配了一个不接受任何参数的方法, 而(..)
匹配了一个接受任意数量参数的方法(零或者更多)。 模式(*)
匹配了一个接受一个任何类型的参数的方法。 模式(*,String)
匹配了一个接受两个参数的方法,第一个可以是任意类型, 第二个则必须是String类型。更多的信息请参阅AspectJ编程指南中语言语义的部分。
1.execution(public * *(..))
任意的公有方法
2.execution(* set*(..))
任意以set开头的方法
3.execution(* com.xyz.service.AccountService.*(..))
com.xyz.service.AccountService接口或者类中的所有方法
4.execution(* com.xyz.service.*.*(..))
com.xyz.service包下的所有类的所有方法
5.execution(* com.xyz.service..*.*(..))
com.xyz.service包及子包下的所有类的所有方法
6.execution(* com.xyz.service.*.*(String,*))
com.xyz.service包及子包下的所有类中的第一个参数类型为String,第二个参数任意的所有方法。
【Java EE 学习 51】【Spring学习第三天】【cglib动态代理】【AOP和动态代理】【切入点表达式】的更多相关文章
- Spring事务管理—aop:pointcut expression 常见切入点表达式及事务说明
Spring事务管理—aop:pointcut expression 常见切入点表达式及事物说明 例: <aop:config> <aop:pointcut expression= ...
- Spring(十九):Spring AOP(三):切面的优先级、重复使用切入点表达式
背景: 1)指定切面优先级示例:有的时候需要对一个方法指定多个切面,而这多个切面有时又需要按照不同顺序执行,因此,切面执行优先级别指定功能就变得很实用. 2)重复使用切入点表达式:上一篇文章中,定义前 ...
- spring的基于xml的AOP配置案例和切入点表达式的一些写法
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.spr ...
- Spring事务管理—aop:pointcut expression 常见切入点表达式及事物说明
例: <aop:config> <aop:pointcut expression="execution(* com.xy.service.*.*(..))" ...
- Java EE 之 过滤器入门学习与总结(1)
使用Filter技术来配合开发会使得开发变得简单起来.简单的一个例子就表现在"乱码问题"上.不使用Filter的话,我们有可能需要为每一个网页设置字符编码集,如request.se ...
- Java EE 之 过滤器入门学习与总结(2)
今天就对使用Filter技术能做什么来个小小的归纳.也为了方便今后的复习. 控制浏览器不进行对jsp页面的缓存 //在doFilter方法中添加这样的代码 HttpServletRequest req ...
- 【Java EE 学习 76 下】【数据采集系统第八天】【通过AOP实现日志管理】【日志管理功能分析和初步实现】
一.日志管理相关分析 1.日志管理是一种典型的系统级别的应用,非常适合使用spring AOP实现. 2.使用日志管理的目的:对系统修改的动作进行记录,比如对权限.角色.用户的写操作.修改操作.删除操 ...
- 深入学习Spring框架(三)- AOP面向切面
1.什么是AOP? AOP为 Aspect Oriented Programming 的缩写,即面向切面编程, 通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术..AOP是OOP的延续, ...
- Spring学习4-面向切面(AOP)之Spring接口方式
一.初识AOP 关于AOP的学习可以参看帮助文档:spring-3.2.0.M2\docs\reference\html目录下index.html的相关章节 1.AOP:Aspect ...
- Spring学习笔记:面向切面编程AOP(Aspect Oriented Programming)
一.面向切面编程AOP 目标:让我们可以“专心做事”,避免繁杂重复的功能编码 原理:将复杂的需求分解出不同方面,将公共功能集中解决 *****所谓面向切面编程,是一种通过预编译方式和运行期动态代理实现 ...
随机推荐
- jenkins持续集成源码管理选项为None,构建失败找不到git.exe解决办法
我的jenkins版本为Jenkins ver. 2.19.1 1.源码管理选项只有None的解决办法: 在插件管理中心,搜索对应的源码管理插件这里以git为例,搜索git plugin点击右下角的安 ...
- 【BZOJ-1419】Red is good 概率期望DP
1419: Red is good Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 660 Solved: 257[Submit][Status][Di ...
- Java多线程干货系列—(一)Java多线程基础
前言 多线程并发编程是Java编程中重要的一块内容,也是面试重点覆盖区域,所以学好多线程并发编程对我们来说极其重要,下面跟我一起开启本次的学习之旅吧. 正文 线程与进程 1 线程:进程中负责程序执行的 ...
- css设置img成圆形
效果图: <img src="test.jpg" /> css代码:img{ width:30px; height:30px; border-radius:50px ...
- mybatis 批量更新
<update id="batchUpdate" parameterType="java.util.List"> <foreach colle ...
- Python Day6
面向对象 概述 面向过程:根据业务逻辑从上到下写垒代码 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可 面向对象:对函数进行分类和封装,让开发"更快更好更强...&qu ...
- 成为java高手的条件
世界上并没有成为高手的捷径,但一些基本原则是可以遵循的. 1.扎实的基础 数据结构.离散数学.编译原理,这些是所有计算机科学的基础,如果不掌握它们,很难写出高水平的程序.程序人人都会写,但当你发现写到 ...
- ubuntu一些基本软件安装方法
ubuntu一些基本软件安装方法 首先说明一下 ubuntu 的软件安装大概有几种方式:1. deb 包的安装方式deb 是 debian 系 Linux 的包管理方式, ubuntu 是属于 deb ...
- zabbix注入过程分析
Zabbix jsrpc.php sql 注入过程分析 漏洞公开详情(https://support.zabbix.com/browse/ZBX-11023)中提示在insertDB() 中的inse ...
- ffmpeg常用转换命令,支持WAV转AMR
音频转换: 1.转换amr到mp3: ffmpeg -i shenhuxi.amr amr2mp3.mp3 2.转换amr到wav: ffmpeg -acodec libamr_nb -i shenh ...