Spring 框架总结
一、Spring框架简介
1、Spring 框架的核心是 AOP 与 IOC (DI) 框架
2、IOC -->控制反转
Spring 将对象封装为 Bean,通过工厂模式实现对象注入,并给对象赋值
2、AOP -->切片
Spring 通过代理模式,可将对象进行拦截切片,从而赋予对象新的能力。
二、Spring管理MyBatis
1、依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.6.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.42</version>
</dependency>
2、Spring-Mybatis.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:tx="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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--jdbc:加载配置参数-->
<context:property-placeholder location="classpath:res/db.properties"/>
<!--druid:创建数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--mybatis:创建sqlSessionFactory对象-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="typeAliasesPackage" value="com.qf.pojo"/>
<property name="mapperLocations" value="mapping/*.xml"/>
</bean>
<!--spring:扫描mapper-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.qf.dao"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
<!--事务:创建JDBC事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--事务管理:标签开发-->
<tx:aspectj-autoproxy proxy-target-class="true"/>
<!--扫描所有的service-->
<context:component-scan base-package="com.qf.service"/>
</beans>
3、db.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/traffic_manager?characterEncoding=utf8
jdbc.username=root
jdbc.password=95107530
三、常用标签
1、@Component -->定义在 Service 实现类上,用于标记为Service,供Spring扫描
2、@AutoWired -->定义在属性上,用于标记属性,供Spring注入Service对象
3、@Transactional(isolation=Isolation.READ_COMMITTED , propagation=Propagation.REQUIRED , readOnly=false , rollbackFor=Exception.class , timeout=-1) —> 开启事务,定义在方法上则该方法带事务,定义在类上则整个类带事务。
@Component
public class ServiceImpl{
@AutoWired
private Serive service;
@Transactional(isolation=Isolation.READ_COMMITTED , propagation=Propagation.REQUIRED , readOnly=false , rollbackFor=Exception.class , timeout=-1)
public void doService(){
service.CURD();
}
}
四、IoC(控制反转)
1、正转
调用者创建对象,controller中有service的实现,service中有dao的实现,你中有我我中有你,容易造成代码的耦合,入侵性强,对代码的健壮性与可扩展性都有影响。
2、反转
通过Spring动态生成一个对象,不需要调用者再去创建对象,入侵性弱,降低模块与模块间的耦合。
3、依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
</dependencies>
4、bean对象
(1)在Spring中将java对象处理为bean对象,只需要在xml文件中写入bean即可创建对象
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="stu" class="com.qf.pojo.Student"></bean>
</beans>
(2)IoC的作用范围(Scope)
a、jar包(2)——》singleton(单例模式)、prototype(多例模式)
b、war包(5)——》request(同一个request中为单例模式)、session(同一个会话中为单例模式
global_Session(同一个web应用中为单例模式)
注:(1)scope默认值为singleton,且为饿汉模式
(2)lazy_init=“true”为懒加载,即不到万不得已不会加载
(3)用标签创建对象,必须有无参构造
5、property
(1)在标签内写标签,通过name,value给对象赋值。
(2)8种基本数据类型以及String类型可直接通过value赋值。
(3)引用类型通过name,ref赋值;ref的值为其他bean的id。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="addr" class="com.qf.pojo.Address">
<property name="area" value="小南"/>
<property name="city" value="小北"/>
<property name="province" value="小兰"/>
</bean>
<bean id="stu" class="com.qf.pojo.Student">
<property name="id" value="1"/>
<property name="name" value="小张"/>
<property name="money" value="1.25"/>
<property name="address" ref="addr"/>
</bean>
</beans>
注:通过标签每个属性必须有setter方法
6、constructor-arg
(1)通过标签创建有参构造器
(2)通过name,value对构造器中的参数赋值
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="birth" class="java.util.Date">
<constructor-arg name="year" value="19"/>
<constructor-arg name="month" value="2"/>
<constructor-arg name="date" value="15"/>
</bean>
<bean id="stu" class="com.qf.pojo.Student">
<constructor-arg name="id" value="1"/>
<constructor-arg name="name" value="小张"/>
<constructor-arg name="money" value="1.25"/>
<constructor-arg type="java.util.Date" ref="birth"/>
</bean>
</beans>
(3)通过index,value对构造器中的参数赋值
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="birth" class="java.util.Date">
<constructor-arg index="0" value="19"/>
<constructor-arg index="1" value="2"/>
<constructor-arg index="2" value="15"/>
</bean>
<bean id="stu" class="com.qf.pojo.Student">
<constructor-arg index="0" value="1"/>
<constructor-arg index="1" value="小张"/>
<constructor-arg index="2" value="1.25"/>
<constructor-arg index="3" ref="addr"/>
<constructor-arg index="4" ref="birth"/>
</bean>
</beans>
(4)通过type,value赋值
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="addr" class="com.qf.pojo.Address">
<property name="area" value="小南"/>
<property name="city" value="小寒"/>
<property name="province" value="小溪"/>
</bean>
<bean name="birth" class="java.util.Date">
<constructor-arg index="0" value="19"/>
<constructor-arg index="1" value="2"/>
<constructor-arg index="2" value="15"/>
</bean>
<bean id="stu" class="com.qf.pojo.Student">
<constructor-arg type="java.lang.Integer" value="1"/>
<constructor-arg type="java.lang.String" value="小张"/>
<constructor-arg type="java.lang.Double" value="1.25"/>
<constructor-arg type="com.qf.pojo.Address" ref="addr"/>
<constructor-arg type="java.util.Date" ref="birth"/>
</bean>
</beans>
注:Spring会根据参数的顺序匹配type,若两个参数的类型相同,则前一个type会匹配参数列表中第一个类型为其对应类型的参数。
(5)通过value或ref直接赋值
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="addr" class="com.qf.pojo.Address">
<property name="area" value="小明"/>
<property name="city" value="校长/>
<property name="province" value="小美"/>
</bean>
<bean name="birth" class="java.util.Date">
<constructor-arg index="0" value="19"/>
<constructor-arg index="1" value="2"/>
<constructor-arg index="2" value="15"/>
</bean>
<bean id="stu" class="com.qf.pojo.Student">
<constructor-arg value="1"/>
<constructor-arg value="小张"/>
<constructor-arg value="1.25"/>
<constructor-arg ref="addr"/>
<constructor-arg ref="birth"/>
</bean>
</beans>
注:通过这种方式赋值必须要注意参数列表的顺序,constructor-arg的顺序必须要与参数列表顺序一致
7、p命名空间
(1)在声明标签的时候可通过p直接对其属性赋值,可不用通过标签赋值
(2)p命名空间
xmlns:p="http://www.springframework.org/schema/p"
注:p为局部变量,可以随意起名,但约定俗成为p
(3)通过 p:属性名=“属性值”对8种基本数据类型以及String类型赋值
(4)通过 p:属性名-ref=“id” 对引用类型赋值
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="addr" class="com.qf.pojo.Address">
<property name="area" value="小寒"/>
<property name="city" value="小韩"/>
<property name="province" value="小溪"/>
</bean>
<bean name="birth" class="java.util.Date">
<constructor-arg index="0" value="19"/>
<constructor-arg index="1" value="2"/>
<constructor-arg index="2" value="15"/>
</bean>
<bean id="stu" class="com.qf.pojo.Student" p:id="1" p:address-ref="addr" p:birthday-ref="birth" p:money="1.25" p:name="小吴"></bean>
</beans>
8、Spring注入数组与集合
(1)数组
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="别名" class="要注入对象的全类名">
<property name="属性名">
<array>
<value>值1</value>
<value>值2</value>
.......
<value>值n</value>
</array>
</property>
</bean>
</beans>
(2)List集合
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="别名" class="全类名">
<property name="属性名">
<list>
<value>值1</value>
<value>值2</value>
.......
<value>值n</value>
</list>
</property>
</bean>
</beans>
(3)Set集合
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="别名" class="全类名">
<property name="属性名">
<set>
<value>值1</value>
<value>值2</value>
.......
<value>值n</value>
</set>
</property>
</bean>
</beans>
(4)Map集合
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="别名" class="全类名">
<property name="属性名">
<map>
<entry key="键1" value="值1" />
<entry key="键2" value="值2" />
.......
<entry key="键n" value="值n"/>
</map>
</property>
</bean>
</beans>
(5)Property
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="别名" class="全类名">
<property name="属性名">
<props>
<prop key="键1">值1</prop>
<prop key="键2">值2</prop>
.......
<prop key="键n">值n</prop>
</props>
</property>
</bean>
</beans>
9、工厂模式注入
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="工厂别名" class="工厂类全类名"></bean>
<bean id="工厂方法别名" factory-bean="工厂别名" factory-method="工厂方法"></bean>
</beans>
注:通过Spring工厂模式注入时,被调用的方法不能是static
五、AoP(代理)
1、代理的概念
代理是在实现原功能的基础上,对原功能的增强。
面向切面编程,即面向我们的关注面,不能让非关注面影响到我们的关注面。而现实场景中非关注面又必不可少,例如获取资源、释放资源、处理异常、记录日志等,太多的非关切面会让关切面的代码变得杂糅,难以维护。此时面向切面编程便是解决此问题的方案,减少非关切面的东西,让我们只专注于核心业务代码
2、静态代理
(1)静态代理需要手动来创建代理类,在代理类中重写原功能的方法的方式对原功能进行加强,其优点为可定制性高,即想增强什么方法就增强什么方法。
public class CalculationProxy implements calculation {
Calculation calculation=null;
public void setCalculation(calculation calculation){
this.calculation=calculation;
}
@Override
public Integer sub(Integer a, Integer b) {
long begin = System.currentTimeMillis();
lazy();
Integer sub = calculation.sub(a, b);
long end = System.currentTimeMillis();
System.out.println("耗时"+(end-begin)+"毫秒");
return sub;
}
@Override
public Integer plus(Integer a, Integer b) {
long begin = System.currentTimeMillis();
lazy();
Integer plus = calculation.plus(a, b);
long end = System.currentTimeMillis();
System.out.println("耗时"+(end-begin)+"毫秒");
return plus;
}
public void lazy(){
Random random=new Random();
try {
Thread.sleep(random.nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
上述代码在进行加减运算功能的基础上添加了计算耗时的功能
注入:1、代理的实质其实是创建一个代理对象,在代理对象中重写需要代理的方法从而实现功能增强的效果
2、工厂类创建的实质是创建代理对象,调用的也是代理对象的代理方法,其对原方法没有改变
(2)静态代理的缺点:若需要代理的方法越多,则需要手写的代码越多。
3、JDK代理
(1)JDK代理是一种动态代理,用JDK提供的类创建代理类,只需要写一个方法即可代理所有需要代理的方法。
public class Factory {
public UserService getUserService(){
UserService us=new UserServiceImpl();
//设置切点,第一个参数为工厂类的类加载器,第二个参数为被代理对象的接口,第三个参数为切面方法
UserService myUs=(UserService) Proxy.newProxyInstance(Factory.class.getClassLoader(), us.getClass().getInterfaces(), new InvocationHandler() {
//切面方法
@Override
//o为代理对象,method为被代理的方法,objects为被代理方法的参数列表
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
MyAspect.before();
Object invoke = method.invoke(us, objects);
System.out.println(invoke);
MyAspect.after();
return invoke;
}
});
//返回的是代理对象
return myUs;
}
}
(2)调用
public void test(){
ApplicationContext ac=new ClassPathXmlApplicationContext("jdkproxy.xml");
//通过Spring工厂注入一个代理对象
UserService userService = ac.getBean("userService", UserService.class);
//通过代理对象调用代理方法
userService.add();
userService.delete(new User(2,"大佬",12.6));
}
注:被动态代理的对象需要接口们,因此被动态代理的对象一定是实现类,且只能代理被实现的方法!
(3)JDK代理的缺点
侵入性强,需要写的代码代码还是太多
4、cglib代理
(1)cglib是一种动态代理,用Spring中framework提供类创建代理对象。
public class Factory {
public UserService getUserService(){
Enhancer enhancer=new Enhancer();
//设置切点
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new MethodInterceptor() {
//切面方法
@Override
//o为代理方法,method为被代理的方法,objects为参数列表,methodProxy为被代理的方法的代理方法
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
MyAspect.before();
Object invoke = method.invoke(new UserServiceImpl(), objects);
System.out.println(invoke);
MyAspect.after();
return invoke;
}
});
//返回代理对象
return (UserService) enhancer.create();
}
}
(2)cglib代理的缺点
没有解决JDK代理侵入性强的问题,代码相对jdk代理来说少了一点,但还是太多
5、ProxyFactoryBean代理
(1)ProxyFactoryBean代理是Spring代理,即将代理对象交由Spring管理,又容器注入一个代理对象;从而解决了侵入性强与代码量过多的问题。
Spring的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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
//定义serviceImpl的bean对象
<bean id="userService" class="com.qf.service.impl.UserServiceImpl"></bean>
//设置拦截器
<bean id="pp" class="com.qf.proxy_factory.MyAspect"></bean>
//设置切点及切面
<bean id="myFactory" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="userService"/>//设置切点
<property name="interfaces" value="com.qf.service.UserService"/>//定义切点的接口们
<property name="interceptorNames" value="pp"/>//定义切面
<property name="optimize" value="true"/>//设置是否使用cglib代理
</bean>
</beans>
拦截器的实现
public class MyAspect implements MethodInterceptor {
public void before(){
System.out.println("这是前置条件");
}
public void after(){
System.out.println("这是后置条件");
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
before();
Object proceed = invocation.proceed();
System.out.println(proceed);
after();
return proceed;
}
}
调用
public class TestProxyFactoryBean {
@Test
public void test(){
ApplicationContext ac=new ClassPathXmlApplicationContext("proxy_factory_bean.xml");
//获取代理对象
UserService factory = ac.getBean("myFactory", UserService.class);
factory.add();
factory.delete(new User(4,"老杨",12.5));
}
}
注:1、拦截器需要实现MethodInterceptor接口的invoke方法
2、optimize是ProxyFactoryBean的祖先类的属性,其代表的含义是是否使用cglib代理
3、除了target是ref外,其他都是value;其中interceptorNames是获得到value后,找到其id为value的bean,通过反射调用其拦截器的方法。
(2)MethodInvocation类的说明
其底层封装了切面所需要的数据,调用起来更为简洁。
(3)ProxyFactoryBean代理还存在的问题
ProxyFactoryBean的切点还不是很精准
6、proxy_target_class代理
(1)proxy_target_class代理也是将代理对象交由Spring容器进行注入,其代码量更简洁,且可定义切点规则,使切点更精确;调用时直接获取service实现类对象,在调用实现类对象方法是通过反射进行实现功能的增强
Spring的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"
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">
//定义serviceImpl的bean对象
<bean id="proxy" class="com.qf.proxy_target.MyAspect"></bean>
//定义拦截器的bean对象
<bean id="userService" class="com.qf.service.impl.UserServiceImpl"></bean>
//通过命名空间,设置切点及切面
<aop:config proxy-target-class="true">//设置强制使用cglib代理
//设置切点的规则
<aop:pointcut id="point" expression="execution(* com.qf.service..*.*(..))"/>
//设置切面
<aop:advisor advice-ref="proxy" pointcut-ref="point"/>
</aop:config>
</beans>
拦截器实现
public class MyAspect implements MethodInterceptor {
public void before(){
System.out.println("这是前置条件");
}
public void after(){
System.out.println("这是后置条件");
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
before();
Object proceed = invocation.proceed();
System.out.println(proceed);
after();
return null;
}
}
调用
public class TestProxyTragetClasss {
@Test
public void test(){
ApplicationContext ac=new ClassPathXmlApplicationContext("proxy_target_class.xml");
//此时获取的代理对象是UserService
UserService proxy = ac.getBean("userService", UserService.class);
//调用代理对象的代理方法时通过反射调用
proxy.add();
proxy.delete(new User(5,"小王",1.25));
}
}
注:此代理需要加入aspectjweaver依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8</version>
(2)不足
虽然切点更精确了,但切面的功能相对来说还是少了点,只有前置切面与后置切面两种
7、proxy_target_class增强代理
(1)、此代理比proxy_target_class代理的功能更多,具有前置、后置、环绕、返回值增强、异常增强的功能;其调用时也是获取serviceImpl对象,通过反射调用其拦截器的增强方法。
Spring的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"
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">
<bean id="userService" class="com.qf.service.impl.UserServiceImpl"></bean>
<bean id="proxy" class="com.qf.proxy_target_class_power.MyAspect"></bean>
//设置切面与切点
<aop:config proxy-target-class="true">
<aop:aspect id="aspect" ref="proxy">
//设置切点规则
<aop:pointcut id="point" expression="execution(* com.qf.service..*.*(..))"/>
<aop:before method="before" pointcut-ref="point"/>//设置前置切面
<aop:after method="after" pointcut-ref="point"/>//设置后置切面
<aop:around method="around" pointcut-ref="point"/>//设置环绕切面
//设置返回值的切面,其returning的值必须要与拦截器中的参数一致
<aop:after-returning method="haveReturn" pointcut-ref="point" returning="object"/>
//设置异常的切面,其returning的值必须要与拦截器中的参数一致
<aop:after-throwing method="throwException" pointcut-ref="point" throwing="throwable"/>
</aop:aspect>
</aop:config>
</beans>
拦截器的实现
public class MyAspect {
//前置切面
public void before(){
System.out.println("这是前置条件");
}
//后置切面
public void after(){
System.out.println("这是后置条件");
}
//环绕切面
public void around(ProceedingJoinPoint joinPoint){
try {
System.out.println("这是环绕的前置条件");
Object proceed = joinPoint.proceed();
System.out.println("这是环绕的后置条件");
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
//返回值切面
public void haveReturn(JoinPoint joinPoint,Object object){
System.out.println("我的返回值为"+object);
}
//异常切面
public void throwException(JoinPoint joinPoint,Throwable throwable){
System.out.println("我抛出的异常为"+throwable.getMessage());
}
}
注:1、其需要什么切面就在Spring的xml文件中定义什么切面
2、返回值切面是当被增强的方法有返回值时才会对其进行增强,异常切面同理
3、环绕切面是在一个方法中即可定义前置切面也可定义后置切面,若同时具有前置切面与后置切面时,其调用的顺序为:前置切面->环绕前置切面->被增强的方法(返回值切面)->环绕的后置切面->后置切面
8、使用注解代理
(1)、使用注解动态代理可以更简便的进行动态代理,只需要在Spring的xml文件中设置好切点与切面后,将切点类与切面类通过<context:compont-scan base-package=“要扫描的包”/>标签扫描进Spring中,并在Aspect类中写需要的切面方法即可。调用时获得serviceImple代理类即可。
Spring的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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.qf.service"/>
<context:component-scan base-package="com.qf.auto"/>
<aop:aspectj-autoproxy/>
</beans>
拦截器方法
package com.qf.auto;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class MyAspect {
@Pointcut(value = "execution(* com.qf.service..*.*(..))")
public void all(){
}
@Before("all()")
public void before(){
System.out.println("这是前置切面");
}
@After("all()")
public void after(){
System.out.println("这是后置切面");
}
@Around("all()")
public void around(ProceedingJoinPoint joinPoint){
try {
System.out.println("环绕前置切面");
Object proceed = joinPoint.proceed();
System.out.println(proceed);
System.out.println("环绕后置切面");
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
@AfterReturning(value = "all()",returning = "obj")
public void returing(JoinPoint joinPoint, Object obj){
System.out.println("我的返回值切面为"+obj);
}
@AfterThrowing(value = "all()",throwing = "obj")
public void throwing(JoinPoint joinPoint,Throwable obj){
System.out.println("我的异常切面为"+obj.getMessage());
}
}
ServiceImple方法的标签设置
package com.qf.service.impl;
import com.qf.pojo.User;
import com.qf.service.UserService;
import org.springframework.stereotype.Component;
@Component("userService")
public class UserServiceImpl implements UserService {
@Override
public User add() {
return new User(1,"张杰",1.25);
}
@Override
public Integer delete(User user) {
System.out.println(user);
return 250;
}
}
注:1、需要注意的是返回值切面与异常切面方法的参数中的Joinpoint类的包,是org.aspectj.lang.JoinPoint包
2、ServiceImpl中的Componet()注解中要加上其id值,getBean时也是获取的是此id值
3、Aspect类中要加入@Aspect标签