文档版本 | 开发工具 | 测试平台 | 工程名字 | 日期 | 作者 | 备注 |
---|---|---|---|---|---|---|
V1.0 | 2016.06.22 | lutianfei | none |
Joinpoint(连接点)
:所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点.Pointcut(切入点)
:所谓切入点是指我们要对哪些Joinpoint进行拦截的定义.Advice(通知/增强)
:所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)Introduction(引介)
:引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.Target(目标对象)
:代理的目标对象Weaving(织入)
:是指把增强应用到目标对象来创建新的代理对象的过程.Proxy(代理)
:一个类被AOP织入增强后,就产生一个结果代理类Aspect(切面)
: 是切入点和通知(引介)的结合public interface UserDao {
public void add();
public void update();
}
public class UserDaoImpl implements UserDao {
public void add() {
System.out.println("添加用户...");
}
public void update() {
System.out.println("修改用户...");
}
}
@Test
public void demo2(){
// 被代理对象
UserDao userDao = new UserDaoImpl();
// 创建代理对象的时候传入被代理对象.
UserDao proxy = new JDKProxy(userDao).createProxy();
proxy.add();
proxy.update();
}
public class JDKProxy implements InvocationHandler{
private UserDao userDao;
public JDKProxy(UserDao userDao) {
super();
this.userDao = userDao;
}
public UserDao createProxy() {
UserDao proxy = (UserDao) Proxy.newProxyInstance(userDao.getClass()
.getClassLoader(), userDao.getClass().getInterfaces(), this);
return proxy;
}
// 调用目标对象的任何一个方法 都相当于调用invoke();
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if("add".equals(method.getName())){
// 记录日志:
System.out.println("日志记录=================");
Object result = method.invoke(userDao, args);
return result;
}
return method.invoke(userDao, args);
}
}
CGLIB(Code Generation Library)是一个开源项目!是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。 Hibernate支持它来实现PO(Persistent Object 持久化对象)字节码的动态生成
CGLIB生成代理机制:CGlib采用非常底层字节码技术,可以为一个类创建子类,解决无接口代理问题
现在做cglib的开发,可以不用直接引入cglib的包.已经在spring的核心中集成cglib.
关于intercept拦截方法
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
… …
}
public class CGLibProxy implements MethodInterceptor{
private ProductDao productDao;
public CGLibProxy(ProductDao productDao) {
super();
this.productDao = productDao;
}
public ProductDao createProxy(){
// 使用CGLIB生成代理:
// 1.创建核心类:
Enhancer enhancer = new Enhancer();
// 2.为其设置父类:
enhancer.setSuperclass(productDao.getClass());
// 3.设置回调:
enhancer.setCallback(this);
// 4.创建代理:
return (ProductDao) enhancer.create();
}
public Object intercept(Object proxy, Method method, Object[] args,MethodProxy methodProxy) throws Throwable {
if("add".equals(method.getName())){
System.out.println("日志记录==============");
Object obj = methodProxy.invokeSuper(proxy, args);
return obj;
}
return methodProxy.invokeSuper(proxy, args);
}
}
//SpringTest
@Test
public void demo2(){
ProductDao productDao = new ProductDao();
ProductDao proxy = new CGLibProxy(productDao).createProxy();
proxy.add();
proxy.update();
}
Spring按照通知Advice在目标类方法的连接点位置,可以分为5类
前置通知 org.springframework.aop.MethodBeforeAdvice
Advisor : Spring中传统切面.代表一般切面,Advice本身就是一个切面,对目标类所有方法进行拦截
Aspect:多个切点和多个通知组合.
PointcutAdvisor : 代表具有切点的切面,可以指定拦截目标类哪些方法(带有切点的切面,针对某个方法进行拦截)
IntroductionAdvisor : 代表引介切面,针对引介通知而使用切面(不要求掌握)
第一步:导入相应jar包.
第二步:编写被代理对象:
第三步:编写增强的代码:
public class MyBeforeAdvice implements MethodBeforeAdvice{
/**
* method:执行的方法
* args:参数
* target:目标对象
*/
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("前置增强...");
}
}
<list>
<value>value>
....
list>
* proxyTargetClass : 是否对类代理而不是接口,设置为true时,使用CGLib代理
* interceptorNames : 需要织入目标的Advice
* singleton : 返回代理是否为单实例,默认为单例
* optimize : 当设置为true时,强制使用CGLib
<bean id="customerDao" class="cn.itcast.spring3.demo3.CustomerDaoImpl">bean>
<bean id="beforeAdvice" class="cn.itcast.spring3.demo3.MyBeforeAdvice">bean>
<bean id="customerDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="customerDao"/>
<property name="proxyInterfaces" value="cn.itcast.spring3.demo3.CustomerDao"/>
<property name="interceptorNames" value="beforeAdvice"/>
bean>
@Autowired
// @Qualifier("customerDao")// 注入是真实的对象,必须注入代理对象.
@Qualifier("**customerDaoProxy**")
private CustomerDao customerDao;
常用PointcutAdvisor 实现类
第一步:创建被代理对象.
第二步:编写增强的类:
public class MyAroundAdvice implements MethodInterceptor{
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("环绕前增强...");
Object result = methodInvocation.proceed();// 执行目标对象的方法
System.out.println("环绕后增强...");
return result;
}
}
<bean id="orderDao" class="cn.itcast.spring3.demo4.OrderDao">bean>
<bean id="aroundAdvice" class="cn.itcast.spring3.demo4.MyAroundAdvice">bean>
<bean id="myPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="patterns" value=".*add.*,.*find.*">property>
<property name="advice" ref="aroundAdvice"/>
bean>
<bean id="orderDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="orderDao">property>
<property name="proxyTargetClass" value="true">property>
<property name="interceptorNames" value="myPointcutAdvisor">property>
bean>
前面的案例中,每个代理都是通过ProxyFactoryBean织入切面代理,在实际开发中,非常多的Bean每个都配置ProxyFactoryBean开发维护量巨大。
自动创建代理(基于后处理Bean.在Bean创建的过程中完成的增强.生成Bean就是代理.)
BeanNameAutoProxyCreator
根据Bean名称创建代理
DefaultAdvisorAutoProxyCreator
根据Advisor本身包含信息创建代理
AnnotationAwareAspectJAutoProxyCreator
基于Bean中的AspectJ 注解进行自动代理
<bean id="customerDao" class="cn.itcast.spring3.demo3.CustomerDaoImpl">bean>
<bean id="orderDao" class="cn.itcast.spring3.demo4.OrderDao">bean>
<bean id="beforeAdvice" class="cn.itcast.spring3.demo3.MyBeforeAdvice">bean>
<bean id="aroundAdvice" class="cn.itcast.spring3.demo4.MyAroundAdvice">bean>
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="*Dao"/>**
<property name="interceptorNames" value="beforeAdvice"/>**
bean>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext2.xml")
public class SpringTest5 {
@Autowired
@Qualifier("orderDao")
private OrderDao orderDao;
@Autowired
@Qualifier("customerDao")
private CustomerDao customerDao;
@Test
public void demo1(){
orderDao.add();
orderDao.delete();
customerDao.update();
}
}
<bean id="customerDao" class="cn.itcast.spring3.demo3.CustomerDaoImpl">bean>
<bean id="orderDao" class="cn.itcast.spring3.demo4.OrderDao">bean>
<bean id="beforeAdvice" class="cn.itcast.spring3.demo3.MyBeforeAdvice">bean>
<bean id="aroundAdvice" class="cn.itcast.spring3.demo4.MyAroundAdvice">bean>
<bean id="myPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">**
<property name="pattern" value=".*add.*"/>
<property name="advice" ref="aroundAdvice"/>
bean>
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">bean>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext3.xml")
public class SpringTest6 {
@Autowired
@Qualifier("orderDao")
private OrderDao orderDao;
@Autowired
@Qualifier("customerDao")
private CustomerDao customerDao;
@Test
public void demo1(){
orderDao.add();
orderDao.update();
orderDao.delete();
customerDao.add();
}
}
AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。
AspectJ是一个基于Java语言的AOP框架
Spring2.0以后新增了对AspectJ切点表达式支持
@AspectJ 是AspectJ1.5新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面
新版本Spring框架,建议使用AspectJ方式来开发AOP
语法:execution(表达式)
execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)
execution(“* cn.itcast.spring3.demo1.dao.*(..)”)
—只检索当前包
execution(“* cn.itcast.spring3.demo1.dao..*(..)”)
—检索包及当前包的子包.
execution(* cn.itcast.dao.GenericDAO+.*(..))
—检索GenericDAO及子类
@Before
前置通知,相当于BeforeAdvice @AfterReturning
后置通知,相当于AfterReturningAdvice @Around
环绕通知,相当于MethodInterceptor @AfterThrowing
抛出通知,相当于ThrowAdvice@After
最终final通知,不管是否异常,该通知都会执行@DeclareParents
引介通知,相当于IntroductionInterceptor (不要求掌握)aspectj依赖aop环境,之前导入的AOP jar包需要继续导入。
第二步:编写被增强的类:
@Aspect
public class MyAspect {
@Before("execution(* cn.itcast.spring3.demo1.UserDao.add(..))")
public void before(){
System.out.println("前置增强....");
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:aspectj-autoproxy />
beans>
开启AspectJ自动代理 :
定义目标类与切面类
<bean id="userDao" class="cn.itcast.spring3.demo1.UserDao">bean>
<bean id="myAspect" class="cn.itcast.spring3.demo1.MyAspect">bean>
/**
* 切面类:就是切点与增强结合
* @author 姜涛
*
*/
@Aspect
public class MyAspect {
@Before("execution(* cn.itcast.spring3.demo1.UserDao.add(..))")
public void before(JoinPoint joinPoint){
System.out.println("前置增强...."+joinPoint);
}
@AfterReturning(value="execution(* cn.itcast.spring3.demo1.UserDao.update(..))",returning="returnVal")
public void afterReturin(Object returnVal){
System.out.println("后置增强....方法的返回值:"+returnVal);
}
@Around(value="MyAspect.myPointcut()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
System.out.println("环绕前增强....");
Object obj = proceedingJoinPoint.proceed();
System.out.println("环绕后增强....");
return obj;
}
@AfterThrowing(value="MyAspect.myPointcut()",throwing="e")
public void afterThrowing(Throwable e){
System.out.println("不好了 出异常了!!!"+e.getMessage());
}
@After("MyAspect.myPointcut()")
public void after(){
System.out.println("最终通知...");
}
@Pointcut("execution(* cn.itcast.spring3.demo1.UserDao.find(..))")
private void myPointcut(){}
}
@Poingcut
进行定义||
进行连接 第一步:编写被增强的类:
第二步:定义切面
public void before(){
System.out.println("前置通知...");
}
<aop:config>
<aop:pointcut expression="execution(* cn.itcast.spring3.demo2.ProductDao.add(..))" id="mypointcut"/>
<aop:aspect ref="myAspectXML">
<aop:before method="before" pointcut-ref="mypointcut"/>
aop:aspect>
aop:config>
public void afterReturing(Object returnVal){
System.out.println("后置通知...返回值:"+returnVal);
}
<aop:config>
<aop:pointcut expression="execution(* cn.itcast.spring3.demo2.ProductDao.add(..))" id="mypointcut"/>
<aop:aspect ref="myAspectXML">
<aop:after-returning method="afterReturing" pointcut-ref="mypointcut" returning="returnVal"/>
aop:aspect>
aop:config>
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
System.out.println("环绕前增强....");
Object result = proceedingJoinPoint.proceed();
System.out.println("环绕后增强....");
return result;
}
<aop:config>
<aop:pointcut expression="execution(* cn.itcast.spring3.demo2.ProductDao.add(..))" id="mypointcut"/>
<aop:aspect ref="myAspectXML">
**<aop:around method="around" pointcut-ref="mypointcut"/>**
aop:aspect>
aop:config>
public void afterThrowing(Throwable e){
System.out.println("异常通知..."+e.getMessage());
}
<aop:config>
<aop:pointcut expression="execution(* cn.itcast.spring3.demo2.ProductDao.add(..))" id="mypointcut"/>
<aop:aspect ref="myAspectXML">
<aop:after-throwing method="afterThrowing" pointcut-ref="mypointcut" throwing="e"/>
aop:aspect>
aop:config>
public void after(){
System.out.println("最终通知....");
}
<aop:config>
<aop:pointcut expression="execution(* cn.itcast.spring3.demo2.ProductDao.add(..))" id="mypointcut"/>
<aop:aspect ref="myAspectXML">
<aop:after method="after" pointcut-ref="mypointcut"/>
aop:aspect>
aop:config>