Spring提供2个代理模式,一个是jdk代理,另一个cglib代理
1.若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。
2.若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。
注意:开发时尽量使用接口的编程,
(1)对接口创建代理优于对类创建代理,因为会产生更加松耦合的系统。
(2)标记为final的方法不能够被通知。spring是为目标类产生子类。任何需要被通知的方法都被重写,将通知织入。final方法是不允许重写的。
(3) spring只支持方法连接点,不支持属性的连接点
要进行AOP编程,首先我们要在spring的配置文件中引入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-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
</beans>
Spring提供了两种切面使用方式,实际工作中我们可以选用其中一种:
l 基于XML配置方式进行AOP开发。
l 基于注解方式进行AOP开发。
SpringAOP编程,需要引入的jar包
l IUserService接口
public interface IUserService {
public void saveUser(String name,String password);
public void updateUser(String name,String password);
public void deleteUser(String name);
public String findUser();
}
l UserServiceImpl实现类
public class UserServiceImpl implements IUserService {
public void saveUser(String name, String password) {
System.out.println("【新增】用户名:"+name+",密码:"+password);
}
public void updateUser(String name, String password) {
System.out.println("【修改】用户名:"+name+",密码:"+password);
}
public void deleteUser(String name) {
System.out.println("【删除】用户名:"+name);
}
public String findUser() {
System.out.println("【查询】用户");
return "小强";
}
}
l 测试类App.java
public class App {
public static void main(String[] args) {
//直接访问目标对象
//使用代理对象访问目标对象,当在spring的容器中添加<aop>
ApplicationContext ac = new ClassPathXmlApplicationContext("cn/itcast/e_xml/a_before/beans.xml");
IUserService userService = (IUserService)ac.getBean("userServiceImpl");
userService.saveUser("超级强", "123");
userService.updateUser("超级强", "123");
userService.deleteUser("超级强");
String str = userService.findUser();
System.out.println("str:"+str);
}
}
l 切面类Security
/**切面*/
public class Security {
/**通知*/
/**
* 任何通知方法可以将第一个参数定义为org.aspectj.lang.JoinPoint类型 (
* 环绕通知需要定义第一个参数为ProceedingJoinPoint类型, 它是 JoinPoint 的一个子类)。
* JoinPoint 接口提供了一系列有用的方法,
* 比如 getArgs()(返回方法参数)、
* getThis()(返回代理对象)、
* getTarget()(返回目标)、
* getSignature()(返回正在被通知的方法相关信息)、
* toString() (打印出正在被通知的方法的有用信息)。
*/
public void checkSecurity(JoinPoint joinPoint){
System.out.println("正在执行验证...");
Object [] args = joinPoint.getArgs();
if(args!=null && args.length>0){
for(Object o:args){
System.out.println("参数:"+o);
}
}
System.out.println("代理对象:"+joinPoint.getThis().getClass());
System.out.println("目标对象:"+joinPoint.getTarget().getClass());
System.out.println("访问目标对象方法的名称:"+joinPoint.getSignature().getName());
System.out.println("spring容器中定义切入点的表达式:"+joinPoint.toString());
if(true){
throw new RuntimeException("抛出运行时异常!");
}
}
}
l Spring容器(beans.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: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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!-- 创建目标对象 -->
<bean id="userServiceImpl" class="cn.itcast.e_xml.a_before.UserServiceImpl"></bean>
<!-- 声明切面 (无灵魂)-->
<bean id="security" class="cn.itcast.e_xml.a_before.Security"></bean>
<!-- spring的aop编程,所有的操作(切面、通知、切入点)都要放置到aop:config -->
<aop:config>
<!--
定义一个切面,此时切面的类具有了灵魂
id:惟一标识
ref:注入对象
-->
<aop:aspect id="aa" ref="security">
<!--
声明切入点
id:切入点的惟一标识
expression:切入点的表达式语言,指定项目中哪个类哪个方法作为切入点
-->
<aop:pointcut id="save" expression="execution(* cn.itcast.e_xml.a_before.UserServiceImpl.saveUser(..))" />
<aop:pointcut id="update" expression="execution(* cn.itcast.e_xml.a_before.UserServiceImpl.updateUser(..))" />
<!--
定义通知(切入点要做的事情)
前置通知:在访问目标对象方法之前,先执行通知定义的方法
特点:如果代理对象(切面)中的方法(通知)抛出异常,此时不会执行目标对象
* pointcut-ref:注入切入点,这样才能让切入点关联通知
* method:指定切面中定义的通知的方法
-->
<aop:before pointcut-ref="save" method="checkSecurity"/>
<aop:before pointcut-ref="update" method="checkSecurity"/>
</aop:aspect>
</aop:config>
</beans>