底层原理
JDK动态代理
jdk代理必须是实现了接口的类
public interface UserDao {
public void save();
public void update();
public void find();
public void delete();
}
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("保存用户");
}
@Override
public void update() {
System.out.println("修改用户");
}
@Override
public void find() {
System.out.println("查询用户");
}
@Override
public void delete() {
System.out.println("删除用户");
}
}
代理类
public class JdkProxy implements InvocationHandler{
//将要增强的对象传递到代理中
private UserDao userDao;
public JdkProxy(UserDao userDao){
this.userDao = userDao;
}
/**
* 产生UserDao代理的方法
* @return
*/
public UserDao createProxy(){
UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(), userDao.getClass().getInterfaces(), this);
return userDaoProxy;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//判断方法名是不是save
if("save".equals(method.getName())){
System.out.println("权限校验");
}
return method.invoke(userDao, args);
}
}
@Test
public void demo(){
UserDao userDao = new UserDaoImpl();
//创建代理
UserDao proxy = new JdkProxy(userDao).createProxy();
proxy.save();
proxy.delete();
proxy.find();
proxy.update();
}
Cglib代理
不需要实现接口
public class CustomerDao {
public void save() {
System.out.println("保存用户");
}
public void update() {
System.out.println("修改用户");
}
public void find() {
System.out.println("查询用户");
}
public void delete() {
System.out.println("删除用户");
}
}
代理类
public class CglibProxy implements MethodInterceptor{
private CustomerDao customerDao;
public CglibProxy(CustomerDao customerDao){
this.customerDao = customerDao;
}
/**
* 使用cglib产生代理的方法
*/
public CustomerDao createProxy(){
//创建cglib的核心对象
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(customerDao.getClass());
//设置回调(类似于InvocationHandler对象)
enhancer.setCallback(this);
//创建代理对象
CustomerDao customerDao = (CustomerDao)enhancer.create();
return customerDao;
}
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//判断方法是否为save
if("save".equals(method.getName())){
//增强
System.out.println("权限校验=================");
}
return methodProxy.invoke(customerDao, args);
}
}
@Test
public void test(){
CustomerDao customerDao = new CustomerDao();
//创建代理
CustomerDao proxy = new CglibProxy(customerDao).createProxy();
proxy.save();
proxy.delete();
proxy.find();
proxy.update();
}
相关术语
public interface ProductDao {
public void save();
public void update();
public void find();
public String delete();
}
Joinpoint:连接点,可以被拦截的点
这里的CRUD方法都可以被增强,这些方法就可以成为连接点
Pointcut:切入点,真正被拦截的点
比如只对save方法增强,在save执行前进行权限校验,那save方法就是切入点
Advice:通知,增强 方法层面的增强
在save执行前进行权限校验,那么权限校验方法就是通知
Introduction:引介,类层面的增强
Target:被增强的对象
对ProductDao进行增强,那么ProductDao就是目标
Weaving:织入,将通知应用到目标的过程
Aspect:切面,多个通知和多个切入点的组合
Proxy:代理对象
AOPxml开发案例
在spring-framework-4.2.4.RELEASE/docs/spring-framework-reference/html/xsd-configuration.html中找到aop开发的约束
<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">
</beans>
目标为是实现接口的类,底层采用的是jdk代理
public interface ProductDao {
public void save();
public void update();
public void find();
public String delete();
}
public class ProductDaoImpl implements ProductDao{
@Override
public void save() {
System.out.println("保存商品");
}
@Override
public void update() {
System.out.println("更改商品");
}
@Override
public void find() {
System.out.println("查找存商品");
}
@Override
public String delete() {
System.out.println("删除商品");
return "测试后置通知的返回值";
}
}
配置目标和切面
<!-- 配置目标对象 -->
<bean id="ProductDao" class="demo3.ProductDaoImpl"></bean>
<!-- 将切面类交给Spring管理 -->
<bean id="MyAspectXML" class="demo3.MyAspectXML"></bean>
通知类型:
前置通知
后置通知
异常抛出通知
最终通知
环绕通知
切面类
public class MyAspectXML {
/**
* 前置通知
*/
public void checkPri(JoinPoint joinPoint){
System.out.println("前置通知======================" + joinPoint);
}
/**
* 后置通知
* 这里的result根据配置文件中returning值而来,与returning的值必须一样,
* 但returning的值可以随便取
*/
public void writeLog(Object result){
System.out.println("后置通知========================" + result);
}
/**
* 环绕通知
* @throws Throwable
*/
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
System.out.println("环绕前通知============");
Object obj = proceedingJoinPoint.proceed();
System.out.println("环绕后通后============");
return obj;
}
/**
* 异常抛出通知
*/
public void afterThrowing(Throwable e){
System.out.println("异常抛出通知" + e.getMessage());
}
/**
* 最终通知---相当于finally代码块中的内容
*/
public void after(){
System.out.println("最终通知");
}
}
配置切面类
<!-- 通过AOP的配置完成对目标类产生代理 -->
<aop:config>
<!-- 表达式配置哪些类的哪些方法需要进行增强 -->
<aop:pointcut expression="execution(* demo3.ProductDaoImpl.save(..))" id="pointcut1"/>
<aop:pointcut expression="execution(* demo3.ProductDaoImpl.delete(..))" id="pointcut2"/>
<aop:pointcut expression="execution(* demo3.ProductDaoImpl.update(..))" id="pointcut3"/>
<aop:pointcut expression="execution(* demo3.ProductDaoImpl.find(..))" id="pointcut4"/>
<!-- 配置切面 -->
<aop:aspect ref="MyAspectXML">
<!-- 前置通知 -->
<aop:before method="checkPri" pointcut-ref="pointcut1"/>
<!-- 后置通知 -->
<aop:after-returning method="writeLog" pointcut-ref="pointcut2" returning="result"/>
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pointcut3" />
<!-- 异常抛出通知 -->
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="e" />
<!-- 最终通知 -->
<aop:after method="after" pointcut-ref="pointcut4" />
</aop:aspect>
</aop:config>
测试类
传统的创建工厂的测试方法
@Test
public void test(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
ProductDao productDao = (ProductDao)applicationContext.getBean(ProductDao.class);
productDao.save();
productDao.update();
productDao.delete();
productDao.find();
}
采用单元测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringDemo3 {
@Resource(name="ProductDao")
private ProductDao productDao;
@Test
public void test(){
productDao.save();
productDao.update();
productDao.delete();
productDao.find();
}
}
AOP注解开发案例
jar包和约束与xml开发一致
这里的目标没有用到接口,底层采用的Cglib代理
public class OrderDao {
public void save(){
System.out.println("保存订单");
}
public void update(){
System.out.println("修改订单");
}
public void delete(){
System.out.println("删除订单");
}
public void find(){
System.out.println("查询订单");
int i = 1 / 0;
}
}
需要在xml中开启aop注解开发,需要配置
<!-- 在配置文件中开启注解的AOP开发(这样就可以在切面类中使用注解) -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
还需要配置目标和切面
<!-- 配置目标类 -->
<bean id="OrderDao" class="demo1.OrderDao"></bean>
<!-- 配置切面类 -->
<bean id="MyAspectAnno" class="demo1.MyAspectAnno"></bean>
在类上加上@Aspect注解,说明这个类是切面类
@Aspect
public class MyAspectAnno {
//前置通知
@Before(value = "execution(* demo1.OrderDao.save(..))")
public void before(){
System.out.println("前置通知================");
}
//后置通知---方法参数必须与returning值一样
@AfterReturning(value = "execution(* demo1.OrderDao.delete(..))",returning="result")
public void AfterReturning(Object result){
System.out.println("后置通知================"+result);
}
//环绕通知
@Around(value = "execution(* demo1.OrderDao.update(..))")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
System.out.println("环绕前================");
Object obj = proceedingJoinPoint.proceed();
System.out.println("环绕后================");
return obj;
}
//异常抛出通知
@AfterThrowing(value = "execution(* demo1.OrderDao.find(..))",throwing="e")
public void afterThrowing(Throwable e){
System.out.println("异常抛出通知================"+e.getMessage());
}
//最终通知
@After(value = "execution(* demo1.OrderDao.find(..))")
public void after(){
System.out.println("最终通知================");
}
/**
* 简写方式:
* 自定义一个方法,比如pointcut1,给这个方法配一个@Pointcut注解,
* 注解的value值如果是execution(* demo1.OrderDao.find(..)),
* 那么用到execution(* demo1.OrderDao.find(..))的地方就可以用【这个类的类名】.pointcut1()替代
*/
@Pointcut(value="execution(* demo1.OrderDao.find(..))")
private void pointcut1(){}
}