Spring - 注解 代理模式 AOP
一、Spring与导包
(一)Spring概述
- 轻量级的DI/IOC与AOP的容器框架
(二)BeanFactory与ApplicationContext的区别
- ApplicationContext是BeanFactory的子类,它拥有更加强大的一些方法(邮件、国际化…),BeanFactory创建的Bean默认是懒加载,ApplicationContext是迫切加载
(三)Maven导包
1.Spring和AOP需要的包
<dependencies>
<!--Spring的核心包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.2.5.RELEASE</version>
</dependency>
<!--Context包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.5.RELEASE</version>
</dependency>
<!--aop的包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.2.5.RELEASE</version>
</dependency>
<!--切面的一个包(织入)-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
<!-- Spring的测试包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.2.5.RELEASE</version>
<!--scope:范围,只能在test包中使用-->
<scope>test</scope>
</dependency>
<!--junit的测试支持-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
2.引入一些插件与资源
<build>
<!--JDK1.8版本-->
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
<!--把配置文件放在java包下面使用(默认不会编译)-->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/test/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
二、依赖注入
(一)构造参数注入
- 标签:
constructor-arg
1.根据构造器参数的顺序(索引)
<!-- 按照索引注入,索引开始为0 -->
<bean id="myBean" class="com.lty._03constructor.MyBean">
<constructor-arg index="0" value="猪皮"></constructor-arg>
<constructor-arg index="1" value="2"></constructor-arg>
</bean>
2.根据构造器参数的名称
<!-- 按照名称注入,名称必须一致 -->
<bean id="myBean" class="com.lty._03constructor.MyBean">
<constructor-arg name="name" value="猪猪"></constructor-arg>
<constructor-arg name="age" value="1"></constructor-arg>
</bean>
3.根据构造器的类型注入
<!-- 按照类型注入,必须一一对应,不能有重复的类型-->
<bean id="myBean" class="com.lty._03constructor.MyBean">
<constructor-arg type="java.lang.String" value="甫甫"></constructor-arg>
<constructor-arg type="java.lang.Integer" value="3"></constructor-arg>
</bean>
4.自动进行注入
<!-- 按照自动注入,顺序是不允许修改的-->
<bean id="myBean" class="com.lty._03constructor.MyBean">
<constructor-arg value="中分"></constructor-arg>
<constructor-arg value="4"></constructor-arg>
</bean>
(二)有一个参数是我们自己的一个对象
1.外部bean引用
<bean id="otherBean" class="com.lty._03constructor.OtherBean"></bean>
<bean id="myBean" class="com.lty._03constructor.MyBean">
<constructor-arg index="0" value="白白"></constructor-arg>
<constructor-arg index="1" value="2"></constructor-arg>
<!--外部bean引用,相当于java中的全局变量,可以重复使用-->
<constructor-arg ref="otherBean"></constructor-arg>
</bean>
2.内部bean配置
<bean id="otherBean" class="com.lty._03constructor.OtherBean"></bean>
<bean id="myBean" class="com.lty._03constructor.MyBean">
<constructor-arg index="0" value="白白"></constructor-arg>
<constructor-arg index="1" value="2"></constructor-arg>
<!--内部bean配置,相当于java中的局部变量-->
<constructor-arg>
<bean class="com.lty._03constructor.OtherBean"></bean>
</constructor-arg>
</bean>
(三)属性注入
1.普通属性
准备属性
private Long id;
private String name;
private Boolean sex;
private BigDecimal salary;
配置的代码
<property name="id" value="1"></property>
<property name="name" value="猪皮"></property>
<property name="sex" value="true"></property>
<property name="salary" value="3000"></property>
2.集合属性
▪ List< String>
准备属性
private List<String> list;
配置的代码
<!--List<String> list:有序可重复-->
<property name="list">
<list>
<value>甫甫</value>
<value>白白</value>
<value>中分</value>
</list>
</property>
▪ Set< String>
准备属性
private Set<String> set;
配置的代码
<!--Set<String> set:无序不可重复-->
<property name="set">
<set>
<value>金木研</value>
<value>利世</value>
<value>董香</value>
</set>
</property>
▪ List< OtherBean>
准备属性
private List<OtherBean> otherBeanList;
配置的代码
<!--List<OtherBean> otherBeanList
有4个值,外部引用的地址值一样
-->
<property name="otherBeanList">
<list>
<!--内部引用-->
<bean class="com.lty._04di.OtherBean"></bean>
<bean class="com.lty._04di.OtherBean"></bean>
<!--外部引用-->
<ref bean="otherBean"></ref>
<ref bean="otherBean"></ref>
</list>
</property>
▪ Set< OtherBean>
准备属性
private Set<OtherBean> otherBeanSet;
配置的代码
<!--Set<OtherBean> otherBeanSet
有3个值,外部引用的地址值只显示了一个
-->
<property name="otherBeanSet">
<set>
<bean class="com.lty._04di.OtherBean"></bean>
<bean class="com.lty._04di.OtherBean"></bean>
<!--外部引用-->
<ref bean="otherBean"></ref>
<ref bean="otherBean"></ref>
</set>
</property>
▪ Map<String,Integer>
准备属性
private Map<String,Integer> map;
配置的代码
<!--Map<String,Integer> map-->
<property name="map">
<map>
<entry key="银时" value="2"></entry>
<entry key="神乐" value="4"></entry>
<entry key="新八" value="6"></entry>
</map>
</property>
3.数组属性
准备属性
private String[] arrays1;
private String[] arrays2;
配置的代码
<!--String[] arrays1:标准写法-->
<property name="arrays1">
<array>
<value>夏</value>
<value>木</value>
</array>
</property>
<!--String[] arrays2:简写形式-->
<property name="arrays2" value="喵咪,老师"></property>
4.Properties属性
准备属性
private Properties props1;
private Properties props2;
配置的代码
<!--Properties props1:标准写法-->
<property name="props1">
<props>
<prop key="username">童</prop>
<prop key="password">123456</prop>
</props>
</property>
<!--Properties props2:简写形式(不支持中文,可以把等号换成空格)-->
<property name="props2">
<value>
username=瑶
password=112233
</value>
</property>
三、XML自动注入(了解)
- 自动注入有两种方式 (类型与名称)
- 设置全局的自动注入
<beans ... default-autowire="byType/byName">
- 单独为某一个bean配置自动注入
<bean ... autowire="byType/byName">
四、全注解
1.配置context命名空间
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
">
2.配置让Spring扫描类与支持注解
<context:component-scan base-package="com.lty._05anno"></context:component-scan>
3.在相应的类上加注解
- @Controller:控制层
- @Service:业务层
- @Repository:持久层
- @Component:组件(一个bean不知道在哪一层用这个)
- @Autowired:注入功能
4.解决接口有多个实现
▪ 先类型注入,再根据名称注入
@Service
public class UserServiceImpl implements IUserService {
/*注解的bean有默认名称,实现类名称首字母小写*/
@Autowired
IUserDao userDaoJDBCImpl;
}
▪ 修改bean的名称
@Repository("userDaoJDBC")
public class UserDaoJDBCImpl implements IUserDao{
@Override
public void save() {
System.out.println("UserDaoJDBCImpl : save");
}
}
@Autowired
IUserDao userDaoJDBC;
}
▪ 在注入bean的时候加上一个限定注解
@Service
public class UserServiceImpl implements IUserService {
@Autowired
@Qualifier("userDaoJDBC")
IUserDao userDaoJDBCImpl;
}
▪ 使用@Resource
@Service
public class UserServiceImpl implements IUserService {
@Resource(name = "userDaoJDBCImpl")
IUserDao userDao;
}
五、代理模式
- 开发术语:抽象主题角色、代理主题(Proxy)角色、真实主题角色
- 分为静态与动态
- Spring使用的是动态代码:JDK(接口)/CGLIB
- 动态代理的代码
//通过JDK的方案创建一个动态代理对象呢
/**
* Foo f = (Foo) Proxy.newProxyInstance(
Foo.class.getClassLoader(),
new Class[] { Foo.class },
handler);
*/
/**
* newProxyInstance:创建代理对象
* 参数一 ClassLoader loader :传入类加载器(随便找一个给它就可以)
* 参数二 Class<?>[] interfaces:抽象主题角色(就是接口对应的类)
* userService.getClass().getInterfaces()
* 参数三 InvocationHandler h:处理器(由我们自己实现)
*/
IUserService proxy = (IUserService)Proxy.newProxyInstance(
ProxyTest.class.getClassLoader(),
userService.getClass().getInterfaces(),
new InvocationHandler() {
//实现的代码
/**
* @param proxy : 代理对象(不用它)
* @param method : 执行方法
* @param args : 执行方法的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
try {
txManager.begin();
//通过反射执行方法
result = method.invoke(userService, args);
txManager.commit();
} catch (Exception e) {
txManager.rollback();
e.printStackTrace();
} finally {
txManager.close();
}
return result;
}
}
);
proxy.save();
}
六、Spring的AOP
- 可以用于事务管理、日志管理、性能监测等
- Spring实现Aop有两种方案:JDK(若目标对象实现了若干接口)与CGLIB(若目标对象没有实现任何接口)
(一)Xml版实现AOP
准备接口:IUserService
public interface IUserService {
void save();
void delete();
}
准备实现 UserServiceImpl
public class UserServiceImpl implements IUserService {
@Override
public void save() {
System.out.println("UserServiceImpl:save");
}
@Override
public void delete() {
System.out.println("UserServiceImpl:delete");
int i = 1 / 0;
}
}
事务管理器对象(模拟的)
public class TxManager {
public void begin(){
System.out.println("开启事务");
}
public void commit(){
System.out.println("提交事务");
}
public void rollback(Throwable throwable){
System.out.println("回滚事务 原因 : " + throwable.getMessage());
}
public void close(){
System.out.println("关闭事务");
}
}
1.添加aop命名空间
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">
2.准备两个bean
<!--创建一个业务对象-->
<bean id="userService" class="com.lty._01aopxml.service.impl.UserServiceImpl"></bean>
<!--创建一个事务对象-->
<bean id="txManager" class="com.lty._01aopxml.TxManager"></bean>
3.xml配置
<aop:config>
<!--
aop:pointcut 配置切点
id 切点名称(可以随便取)
expression 表达式execution(),定位到接口它的实现也全部找到
*:任意返回值
com.lty._01aopxml.service:定位的包名
I*Service:以I开始以Service结尾的所有接口类
*:任意方法
(..):任意参数
-->
<aop:pointcut id="pointcut" expression="execution(* com.lty._01aopxml.service.I*Service.*(..))"></aop:pointcut>
<!--
aop:aspect 切面(由切点与增强【通知】组成)
ref="txManager" 引入增强的类
何地:在哪些类的哪些方法中:pointcut
何时:在方法执行之前:before
做什么事:执行txManager这个bean中的方法
-->
<aop:aspect ref="txManager">
<!-- 前置通知 -->
<aop:before method="begin" pointcut-ref="pointcut"></aop:before>
<!-- 后置通知 -->
<aop:after-returning method="commit" pointcut-ref="pointcut"></aop:after-returning>
<!-- 异常通知(异常抛出去才能使用) -->
<aop:after-throwing method="rollback" pointcut-ref="pointcut" throwing="throwable"></aop:after-throwing>
<!-- 最终通知 -->
<aop:after method="close" pointcut-ref="pointcut"></aop:after>
</aop:aspect>
</aop:config>
环绕通知
- 前面的4个通知就不用了,其他一样
事务管理器对象(模拟的)
public class TxManager {
public void begin(){
System.out.println("开启事务");
}
public void commit(){
System.out.println("提交事务");
}
public void rollback(Throwable throwable){
System.out.println("回滚事务 原因 : " + throwable.getMessage());
}
public void close(){
System.out.println("关闭事务");
}
/*环绕通知*/
public void around(ProceedingJoinPoint joinPoint){
try{
begin();
//执行方法
joinPoint.proceed();
commit();
}catch (Throwable throwable){
rollback(throwable);
throwable.printStackTrace();
}finally {
close();
}
}
}
xml配置
<aop:aspect ref="txManager">
<!--环绕通知(可以自己控制执行顺序)-->
<aop:around method="around" pointcut-ref="pointcut"></aop:around>
</aop:aspect>
(二)注解版实现AOP
- 需要在相应的bean上加注解
- 配置包的扫描
- 所有的配置都是在TxManager加注解
xml配置
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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
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.lty._02aopanno"></context:component-scan>
<!--支持AOP注解-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
事务管理器TxManager
- 切点的名字是方法名()
/**
* @Aspect = <aop:aspect ref="txManager">
*/
@Component
@Aspect
public class TxManager {
/*配置一个切点(随便写个方法)
*<aop:pointcut id="pointcut()" expression="execution()"></aop:pointcut>
*/
@Pointcut("execution(* com.lty._02aopanno.service.I*Service.*(..))")
public void pointcut(){
}
/*<aop:before method="begin" pointcut-ref="pointcut"></aop:before>*/
@Before("pointcut()")
public void begin(){
System.out.println("开启事务");
}
/*<aop:after-returning method="commit" pointcut-ref="pointcut"></aop:after-returning>*/
@AfterReturning("pointcut()")
public void commit(){
System.out.println("提交事务");
}
/*<aop:after-throwing method="rollback" pointcut-ref="pointcut" throwing="throwable"></aop:after-throwing>*/
@AfterThrowing(value = "pointcut()",throwing = "throwable")
public void rollback(Throwable throwable){
System.out.println("回滚事务 原因 : " + throwable.getMessage());
}
/*<aop:after method="close" pointcut-ref="pointcut"></aop:after>*/
@After("pointcut()")
public void close(){
System.out.println("关闭事务");
}
}
环绕通知
- 要把其它几个通知的注解去掉(不然会出现重复)
/**
* @Aspect = <aop:aspect ref="txManager">
*/
@Component
@Aspect
public class TxManager {
public void pointcut(){}
public void begin(){
System.out.println("开启事务");
}
public void commit(){
System.out.println("提交事务");
}
public void rollback(Throwable throwable){
System.out.println("回滚事务 原因 : " + throwable.getMessage());
}
public void close(){
System.out.println("关闭事务");
}
/*环绕通知
*<aop:around method="around" pointcut-ref="pointcut"></aop:around>
*/
@Around("pointcut()")
public void around(ProceedingJoinPoint joinPoint){
try {
begin();
joinPoint.proceed();
commit();
}catch (Throwable throwable){
rollback(throwable);
throwable.printStackTrace();
}finally {
close();
}
}
}
七、创建Bean的方式
(一)实例化一个有公共无参构造的方式
<bean id="myBean" class="cn.itsource._09_createbean._01_base.MyBean" />
(二)FactoryBean方式
- 一个类实现了FactoryBean接口
- 执行getObject方式,返回的值也会变成一个Bean
准备一个FactoryBean
/**
* 一个类实现FactoryBean接口,那么它就是一个FactoryBean
* 泛型就是它要操作的那个类型
*/
public class EntityManagerFactoryBean implements FactoryBean<EntityManagerFactory> {
//这个方法返回什么对象,我们拿到的bean就是什么对象
@Override
public EntityManagerFactory getObject() throws Exception {
return Persistence.createEntityManagerFactory();
}
//返回的对象类型
@Override
public Class<?> getObjectType() {
return EntityManagerFactory.class;
}
//这个对象是否是单例的
@Override
public boolean isSingleton() {
return true;
}
}
配置对应的方法
<bean id="entityManagerFactory" class="cn.itsource._09_createbean._02_factorybean.MyEntityManagerFactoryBean">
</bean>
BeanFactory与FactoryBean的区别
- BeanFactory接口 : 顶层父工厂,容器,获取bean的实例
- FactoryBean接口:实例化不能通过默认无参构造方法获取的bean,通过子类的getObject方法来返回实例
(三)集成静态简单工厂
- 工厂中有一个静态方法
- 直接拿到这个方法中返回的对象(bean)
准备一个静态工厂
public class DaoFactory {
//在这个工厂中有一个静态方法
public static MyDao createMyDao(){
return new MyDao();
}
}
配置这个工厂对应的方法
<bean id="myDao" class="cn.itsource._10_bean.DaoFactory"factory-method="createMyDao"></bean>
(四)集成实例简单工厂
- 工厂中有个方法
- 直接拿到这个方法中返回的对象(bean)
准备一个静态工厂
public class DaoFactory {
public MyDao createMyDao(){
return new MyDao();
}
}
配置这个工厂对应的方法
<!--工厂的一个方法(非静态)-->
<bean id="daoFactory" class="cn.itsource._10_bean.DaoFactory" />
<bean id="myDao" factory-bean="daoFactory" factory-method="createMyDao" />