Spring-构造注入&注解注入&代理模式&AOP
1. 课程介绍
- 1. 依赖注入;(掌握)
- 2. XML自动注入;(掌握)
- 3. 全注解配置;(掌握)
- 4. 代理模式;(掌握)
- 5. AOP;(掌握)
- 依赖注入;(掌握)
2.1. 构造参数注入
自己用得不多,除非是其他框架提供的类可能会用到
常用的是方案一、二
2.1.1. 方案一:根据构造器参数的顺序(索引)
<!-- 按照索引注入,索引开始为0 -->
<bean id="MyBean" class="cn.itsource._01_.MyBean">
<constructor-arg index="0" value="666" />
<constructor-arg index="1" value="张二娃" />
</bean>
2.1.2. 方案二:根据构造器参数的名称
<!-- 按照名称注入,名称必须一致,类似set注入类似 -->
<bean id="MyBean" class="cn.itsource._01_.MyBean">
<constructor-arg name="id" value="1" />
<constructor-arg name="name" value="张三娃" />
</bean>
2.1.3. 方案三:根据构造器的类型注入
<!-- 按照类型注入,必须一一对应,不能有重复的类型-->
<bean id="MyBean" class="cn.itsource._01_.MyBean">
<constructor-arg type="java.lang.Long" value="1" />
<constructor-arg type="java.lang.String" value="张四娃" />
</bean>
2.2. 如果有一个参数是我们自己的一个对象,怎么解决?
2.2.1. 方案一:先在外面定义好
<bean id="otherBean" class="cn.itsource._01_.OtherBean"/>
<bean id="MyBean" class="cn.itsource._01_.MyBean">
<constructor-arg value="1" />
<constructor-arg value="张五娃" />
<constructor-arg ref="otherBean"/>
</bean>
2.2.2. 方案二: 使用一个内部的Bean完成(不需要加id)
<bean id="MyBean" class="cn.itsource._01_.MyBean">
<constructor-arg value="1" />
<constructor-arg value="张六娃" />
<constructor-arg>
<bean class="cn.itsource._01_.OtherBean"/>
</constructor-arg>
</bean>
2.3. 其它简单、集合属性注入
2.3.1. 准备好相应的类属性
// 简单属性
private Long id;
private String name;
private Boolean sex;
private BigDecimal salary;
// 对象属性
private List<OtherBean> otherBeanList;
private Set<String> set;
private Set<OtherBean> otherBeanSet;
private Map<String,Object> map;
//下面这个是重点
private Properties props1;
private Properties props2;
private String[] arrays;
private List<String> list;
下面的都看一下,大概了解一下就行了
2.4. 数组(两种方案-掌握):
2.4.1. 简写
<property name="arrays" value="A,B,C" />
2.4.2. 完整写法
<property name="arrays">
<array>
<value>xxx</value>
<value>yyy</value>
<value>zzz</value>
</array>
</property>
2.5. List<String>(了解)
<property name="list">
<list>
<value>xxx</value>
<value>aaa</value>
<value>bbbb</value>
</list>
</property>
2.6. Set<String>(了解)
<property name="set">
<set>
<value>xxx</value>
<value>aaa</value>
<value>bbbb</value>
</set>
</property>
2.7. List<OtherBean>(了解)
<property name="otherBeanList">
<list>
<bean class="cn.itsource._01_.OtherBean" />
<bean class="cn.itsource._01_.OtherBean" />
<ref bean="otherBean" />
<ref bean="otherBean" />
</list>
</property>
2.8. Set<OtherBean>(了解)
<property name="otherBeanSet">
<set>
<bean class="cn.itsource._01_.OtherBean" />
<bean class="cn.itsource._01_.OtherBean" />
<ref bean="otherBean" />
<ref bean="otherBean" />
</set>
</property>
2.9. Map(了解)
<property name="map">
<map>
<entry key="xx" value="value1"></entry>
<entry key="yy" value="value2"></entry>
</map>
</property>
2.10. 这里是咱们的重点部分,怎么配置一个Properties对象:
2.10.1. 方案一:简单,不支持中文
<property name="props1">
<value>
Jpa.dialect=org.Jpa.dialect.HSQLDialect
Jpa.driverClassName=com.mysql.jdbc.Driver
</value>
</property>
2.10.2. 方案二:支持中文
<property name="props2">
<props>
<prop key="Jpa.dialect">org.Jpa.dialect.HSQLDialect</prop>
<prop key="Jpa.driverClassName">com.mysql.jdbc.Driver中文 </prop>
</props>
</property>
- XML自动注入
使用XML自动注入,可以简化我们XML的配置。
不过对于学习阶段,并不建议大家这样来使用。
3.1. 简化spring的配置文件
3.1.1. byName 按照属性的名称 setXxx方法
bean.id的名称
3.1.2. byType 按照注入对象的类型,要求:类型只能配置一个实例
setXxx方法 注入类的类型(Class)
和配置文件里面的类型进行比较
配置文件里面的类型只能是1个
3.2. 配置方案
根节点beans default-autowire="byName" 对当前配置文件的所有bean都生效
子节点bean autowire="byType"只对当前bean生效
- 全注解配置
在Java中写Xml配置Bean还是比较麻烦,因此,Spring还提供了使用注解来配置的文件。我们可以来看一下,注解是怎么来配置这个Xml的
配置使用注解流程步骤
4.1. 配置context命名空间
<?xml version="1.0" encoding="UTF-8"?>
<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">
4.2. 第一步:配置让Spring扫描类与支持注解
<!--
@Component 普通仓库
@Repository 仓库(其实就是dao层)
@Service service层
@Controller 控制层(servlet、admin)
-->
<!-- 进行包的扫描,去看类上面是否有相应的标签配置 -->
<context:component-scan base-package="cn.itsource._03_anno" />
<!-- 这个不是必须的(spring3.2版本前使用) 配上后兼容性好 -->
<context:annotation-config />
4.3. 第二步:在类里面加上注解
4.3.1. Dao
@Repository
public class UserDao {
public void save(){
System.out.println("保存数据xxxx....");
}
}
4.3.2. Service
@Service
public class UserService {
@Autowired
private UserDao userDao;
public void save(){
userDao.save();
}
}
4.3.3. Action
@Controller
@Scope(“prototype”) 多例
public class UserAction {
@Autowired
private UserService userService;
public String save(){
userService.save();
return "success";
}
}
4.4. 注解配置bean的名称
4.4.1. 接口有两个实现(可能性比较少)
完成之后,大家注意,现在的配置还有一个问题,比如说我按真实情况来一个Dao接口,然后这个接口有两个实现,现在就会出问题
IUserDao (UserJdbcDaoImpl/UserJpaDaoImpl)
而我们声明的时候就是使用了IUserDao
@Service
public class UserService {
@Autowired
private IUserDao userDao;
public void save(){
userDao.save();
}
}
此时会就出错:因为Spring不知道你用的是哪一个dao的实现
4.4.2. 解决的方案:为dao生成的bean加上名称,我们在调用的时候确定名称即可。
@Repository("userJdbcDao")
public class UserJdbcDaoImpl implements IUserDao{
public void save(){
System.out.println("保存数据xxxx....");
}
}
@Repository("userJpaDao")
public class UserJpaDaoImpl implements IUserDao{
public void save(){
System.out.println("保存数据xxxx....");
}
}
4.5. 调用名称两套方案:
4.5.1. 方案一:使用@Autowired
@Service
public class UserService {
@Autowired
@Qualifier("userJdbcDao")
private IUserDao userDao;
public void save(){
userDao.save();
}
}
4.5.2. 方案二:使用@Resource
@Service
public class UserService {
@Resource(name="userJpaDao")
private IUserDao userDao;
public void save(){
userDao.save();
}
}
4.6. 问题:使用哪一种注解来解决比较好?
如果你用的是Spring的注解,尽量全部使用Spring的注解标签
- 使用xml版手动注入,xml自动注入还是注解版?
初学使用xml版,手动注入
熟练之后可以使用xml自动注入
以后趋势:注解版
如果是注解版,就全部使用注解(自己写的类),不要混用,其它框架已经写好的类仍然使用xml进行配置
如果是xml版,在测试的使用必须使用注解注入(因为测试框架使用注解版)
只有自己写的类,才可以全部用注解,别人写的类使用注解必须去修改别人的源码,添加相应的注解标志(不建议这样搞)
- 代理模式
Aop利用代理模式实现功能增强
Aop能够做什么?日志管理,事务管理,权限控制
6.1. 定义
代理模式的英文叫做Proxy或Surrogate,中文都可译为”代理“,所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用
6.2. 三种角色
6.2.1. 抽象主题角色:
声明了真实主题和代理主题的共同接口,这样一来在任何可以使用真实主题的地方都可以是使用代理主题
6.2.2. 代理主题(Proxy)角色:
代理主题角色内部含有对真实主题的引用,从而可以在任何时候操作真实主题对象;
代理主题角色提供一个与真实主题角色相同的接口,以便可以在任何时候都可以替代真实主题控制对真实主题的引用,负责在需要的时候创建真实主题对象(和删除真实主题对象);
代理角色通常在将客户端调用传递给真实的主题之前或之后(前置增强/通知,后置增强/通知),都要执行某个操作,而不是单纯地将调用传递给真实主题对象。
6.2.3. 真实主题角色:
定义了代理角色所代表地真实对象
6.3. 代理模式分类
静态代理/动态代理
6.4. 静态代理
6.4.1. 抽象主题角色
6.4.2. 真实主题角色
6.4.3. 代理主题角色:中介
代理主题角色内部含有对真实主题的引用
6.4.4. 调用方
如果有代理主题角色存在,必须修改调用方才能实现代理
6.4.5. 静态代理的小结
如果有n个接口,必然有n个实现,n个代理类:代码非常多
6.5. jdk动态代理
和静态代理有相同的抽象主题角色,真实主题角色
JDK动态代理要求它代理的这个真实对象必须要实现某一个接口
CGLIB 动态代理,真实对象可以不实现接口
Spring中使用代理,如果这个类有接口,默认使用JDK的动态代理,如果这个类没有接口,会使用CGLIB的动态代理
6.5.1. 代理主题角色
6.5.2. 调用方
6.5.3. jdk动态代理小结
如果有n个接口,必然有n个实现,只用写1个代理类JDKProxy就可以对所有有接口进行处理
如果有代理主题角色存在,必须修改调用方才能实现代理
6.6. Cglib动态代理
Cglib类似于javassist-3.18.1-GA.jar功能字节码增强,
原来Hibernate3.2之前就是使用cglib来进行字节码增强
6.6.1. 代理主题角色
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
6.6.2. cglib动态代理小结
只用写1个代理类CglibProxy就可以对所有没有接口的并且不能是final类都进行处理
如果有代理主题角色存在,必须修改调用方才能实现代理
- Spring的AOP
7.1. 早期aop都是spring自己实现,后来还是用的标准aspects
AOP:面向切面编程
配置的三个元素(何时,何地,执行什么功能)
何时:在执行方法之前/之后/有异常... 前置还是后置,异常
何地:在哪一个包中的哪一个类的哪一个方法上面执行 切入点:哪个包哪个类哪个方法
做什么事: 在UserServie中执行update方法之前添加日志 增强/通知
7.2. Spring有两种代理方案:
7.2.1. 若目标对象实现了若干接口
spring使用JDK的java.lang.reflect.Proxy类代理。
7.2.2. 若目标对象没有实现任何接口,
spring使用CGLIB库生成目标对象的子类。
7.2.3. 使用该方案时需要注意:
1.对接口创建代理优于对类创建代理,因为会产生更加松耦合的系统。
对类代理是让遗留系统或无法实现接口的第三方类库同样可以得到通知,
这种方案应该是备用方案。
2.标记为final的方法不能够被通知。spring是为目标类产生子类。任何需要
被通知的方法都被复写,将通知织入。final方法是不允许重写的。
7.3. Xml版
7.3.1. 添加jar文件
aspects
spring-framework-4.1.2.RELEASE-dependencies\org.aopalliance\com.springsource.org.aopalliance\1.0.0\com.springsource.org.aopalliance-1.0.0.jar
spring-framework-4.1.2.RELEASE-dependencies\org.aspectj\com.springsource.org.aspectj.weaver\1.6.8.RELEASE\com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
7.3.2. 添加aop命名空间
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
7.3.3. 前置增强(通知)
7.3.4. 后置增强,异常增强,最终增强
7.3.5. 环绕增强(前面的4个增强就不用了)
7.3.6. 环绕方法
import org.aspectj.lang.ProceedingJoinPoint;
7.4. 注解版
7.4.1. 配置文件
7.4.2. 事务管理器
7.4.3. AOP中的概念
什么叫做AOP:Aspect oritention programming(面向切面编程)
AOP当中的概念:
1,切入点Pointcut:在哪些类,哪些方法上面切(where);
2,增强,通知Advice(对应到一个方法):在方法的什么时机(when)做什么(what);
3,切面Aspect:切入点+通知
4,织入Weaving:把切面加入到对象,并创建出代理对象的过程.