=========== 用 Annotation的方式 实现 AOP =================
1、spring依赖库
* SPRING_HOME/dist/spring.jar
* SPRING_HOME/lib/jakarta-commons/commons-logging.jar
* SPRING_HOME/lib/log4j/log4j-1.2.14.jar
* SPRING_HOME/lib/aspectj/*.jar
2、采用Aspect定义切面
3、在Aspect定义Pointcut和Advice
4、启用AspectJ对Annotation的支持并且将Aspect类和目标对象配置到Ioc容器中
注意:在这种方法定义中,切入点的方法是不被执行的,它存在的目的仅仅是为了重用切入点
即Advice中通过方法名引用这个切人点
AOP术语(对应下面的例子 理解):
* Cross cutting concern // 做检查安全性这个需求 就是 一个 横切关注点
* Aspect // SecurityHandler类 就是一个 方面aspect
* Advice // checkSecurity()方法
* Pointcut // add*方法 delete*方法 等等的方法
* Joinpoint //
* Weave // advice 应用到对象的过程 就叫做weave
* Target Object // UserManagerImpl
* Proxy // 本例中,从ioc容器中 拿出的usermanager类,就是代理
* Introduction // 了解一下,作用是 动态添加方法
本节的例子:
UserManager.java
public interface UserManager {
public void addUser(String username, String password);
public void deleteUser(int id);
public void modifyUser(int id, String username, String password);
public String findUserById(int id);
}
UserManagerImpl .java
public class UserManagerImpl implements UserManager {
public void addUser(String username, String password) {
System.out.println("-------UserManagerImpl.addUser()----------");
}
public void deleteUser(int id) {
System.out.println("-------UserManagerImpl.deleteUser()----------");
}
public String findUserById(int id) {
System.out.println("-------UserManagerImpl.findUserById()----------");
return null;
}
public void modifyUser(int id, String username, String password) {
System.out.println("-------UserManagerImpl.modifyUser()----------");
}
}
SecurityHandler.java
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class SecurityHandler {
/**
* 定义Pointcut,Pointcut的名称就是allAddMethod,此方法不能有返回值和参数,该方法只是一个
* 标识
*
* Pointcut的内容是一个表达式,描述那些对象的那些方法(订阅Joinpoint)
*/
@Pointcut("execution(* add*(..)) || execution(* del*(..))")
// 上面 这句话 很重要:意思是
// 1、在ioc中配置的某一个类,如果这个类中包含有 和 add* 或 del* 匹配的方法,这个类就被代理。
// 2、在ioc中配置的某一个类,如果这个类中 不包含 add* 或 del* 匹配的方法,ioc就不生成这个类的代理。
private void allAddMethod(){};
/**
* 定义Advice,标识在那个切入点何处织入此方法
*/
@Before("allAddMethod()")
private void checkSecurity() {
System.out.println("----------checkSecurity()---------------");
}
}
Client.java
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Client {
public static void main(String[] args) {
BeanFactory factory = new ClassPathXmlApplicationContext("applicationContext.xml");
// 这里 从 ioc中拿出的 是 代理
UserManager userManager = (UserManager)factory.getBean("userManager");
// 这里 从 ioc中拿出的 是 代理
UserManager um = (UserManager)factory.getBean("um");
userManager.addUser("张三", "123");
um.addUser("adf", "111");
}
}
applicationContext.xml
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<aop:aspectj-autoproxy/>
<bean id="securityHandler" class="test.aop.SecurityHandler"/>
<bean id="userManager" class="test.aop.UserManagerImpl"/> // 这里 拿出来就是 代理,因为这个类中,有和pointcut指定的方法 相匹配的方法
<bean id="um" class="test.aop.UserManagerImpl"/> // 这里 拿出来 就是 代理,因为这个类中,有和pointcut指定的方法 相匹配的方法
</beans>
PS:下面的地方 要好好理解
@Pointcut("execution(* add*(..)) || execution(* del*(..))")
// 上面 这句话 很重要:意思是
// 1、在ioc中配置的某一个类,如果这个类中包含有 和 add* 或 del* 匹配的方法,这个类就被代理。
// 2、在ioc中配置的某一个类,如果这个类中 不包含 add* 或 del* 匹配的方法,ioc就不生成这个类的代理。
=========== 用 配置文件 的方式 实现 AOP =================
这种方式 和 上一种方式,只有形式的差别,本质完全一样。
看完上个例子,很容易理解这个例子。
UserManager.java
UserManagerImpl .java
Client.java
这三个类 不用变。
SecurityHandler.java 类变的超级简单,之前annotation的作用 完全 转移到 applicationContext.xml中。
public class SecurityHandler {
private void checkSecurity() {
System.out.println("----------checkSecurity()---------------");
}
}
applicationContext.xml
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<bean id="securityHandler" class="test.aop.SecurityHandler"/>
<bean id="userManager" class="test.aop.UserManagerImpl"/>
<aop:config>
<aop:aspect id="security" ref="securityHandler">
<aop:pointcut id="allAddMethod" expression="execution(* test.aop.UserManagerImpl.add*(..))"/>
<aop:before method="checkSecurity" pointcut-ref="allAddMethod"/>
</aop:aspect>
</aop:config>
</beans>
粗体的部分,对照之前的 annotation 比较。完全 体现 了同样的信息。
=========== 在advice方法中,如何拿到关于被代理类的更多信息 =================
import org.aspectj.lang.JoinPoint;
public class SecurityHandler {
private void checkSecurity(JoinPoint joinPoint) {
// 这里是 取得 参数
Object[] args = joinPoint.getArgs();
for (int i=0; i<args.length; i++) {
System.out.println(args[i]);
}
// 这里是 取得 方法的名字
System.out.println(joinPoint.getSignature().getName());
System.out.println("----------checkSecurity()---------------");
}
}
=========== CGLIB =================
1、被代理的类 实现了某种 接口 的情况下,默认是jdk实现动态代理。
被代理的类 实现了某种 接口 的情况下,可以强制使用cglib来实现动态代理。
2、被代理的类 没有实现某种接口 的情况下,只能使用cglib来生成动态代理。
例子1(实现接口时,强制使用cglib代理):
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<aop:aspectj-autoproxy proxy-target-class="true"/>
<bean id="securityHandler" class="com.bjsxt.spring.SecurityHandler"/>
<bean id="userManager" class="com.bjsxt.spring.UserManagerImpl"/>
<aop:config>
<aop:aspect id="security" ref="securityHandler">
<aop:pointcut id="allAddMethod" expression="execution(* com.bjsxt.spring.UserManagerImpl.add*(..))"/>
<aop:before method="checkSecurity" pointcut-ref="allAddMethod"/>
</aop:aspect>
</aop:config>
</beans>
注意粗体的语句,说明了:代理类的生成 强行指定cglib生成。在client.java类中,断点调试可以清楚的看到效果。
例子2(没有实现接口时,spring会自动的使用cglib 来生成代理):
这里需要做的 只是 1、把 UserManager.java接口去掉。2、把强行指定那句化删掉 其他不变。
spring 回自动判断 使用 jdk动态代理 还是 cglib代理。
当然 推荐使用的 还是 jdk动态代理了。