Spring
Spring入门:
IOC(Inversion of Control)底层实现原理:
将原本在程序中手动创建对象的权力交给spring
DI(DI—Dependency Injection)
依赖注入spring在创建对象的过程中,将这个对象所拥有的属性注入进去。
SpringBean管理
- spring工厂类
- SpringBean管理(XML方式)
- Spring的属性注入(XML方式)
- SpringBean管理(注解方式)
- Spring的属性注入(注解方式)
Spring 工厂类:ApplicationContext()
结构:
ClassPathXmlApplicationContext:加载类路径下的配置文件
FileSystemXmlApplicationContext:加载文件系统中的配置文件
1. 工厂类 BeanFactory 和 ApplicationContext 的区别。
-
ApplicationContext 是 BeanFactory 的子接口,提供了比父接口更多的功能。
-
在生成 bean 实例的时候,生成的时机是不一样的。
BeanFactory 在工厂实例化后,在调用 getBean 时创建实例。
ApplicationContext 在一加载配置文件的时候,将配置文件中所有单例模式生成的类全部实例化。
现在一般使用 ApplicationContext,不建议使用 BeanFactory。
SpringBean管理(XML方式)
Bean实例化的三种方式:
-
使用类的构造器实例化(默认无参构造)
-
使用静态工厂方法实例化(简单工厂模式)
-
使用实例工厂方法实例化(工厂方法模式)
Bean的常用配置:
id和name:
一般情况下在装配一个Bean时,通过指定一个id属性作为Bean的名称。
id属性在ioc容器中必须是唯一的。
如果Bean名称 中有特殊字符,就要使用name属性。
class:
用于设置一个类的全路径,主要作用是IOC容器生成类的实例。
Bean的作用域:scope
Bean的生命周期:
init-method和destory-method属性:
Bean的完整生命周期:
BeanProcessor:
Spring属性注入;
-
构造方法注入 --spring支持
-
setter方法注入 --spring支持(实际开发中更习惯于用)
-
p名称空间注入 --spring2.5以后引入的
-
接口注入
-
SpEL(Spring expression language,spring表达式语言)注入
<bean id ="" value=" #{表达式}">
SpEL表达式:语法#{}
#{‘hello'} --使用字符串
#{beanId} --使用另一个bean
#{beanId.connect.toUpperCase()} --使用指定名属性,并使用方法
#{T(java.lang.Math).PI} --使用静态字段或方法
复杂类型的属性注入
-
数组类型的注入
-
List集合类型的属性注入
-
Set类型的属性注入
-
Map类型的属性注入
-
properties类型注入
SpringBean管理(注解方式)
实例化Bean有四个注解
@Component
@Repository --持久层
@Service --业务层
@Colltroller --WEB层
bean的作用域配置
Spring中可以使用scope属性来配置bean的作用域:
singleton:单例,在初始化配置文件时生成单例bean对象
prototype:原型的,在初始化配置文件时不生成bean对象,使用时返回不同的bean对象
request:web环境下每一个request请求都会返回一个不同的bean,只在本次请求中有效
session:web环境下每一个request请求都会返回一个不同的bean,在session中有效
属性(对象)注入(注解方式)
@value
简单属性注入:有setter()需要将@value注解放在setter()方法上;无setter()放在属性上。
@AutoWired
自动注入:默认按照类型注入;
—如果两个相同类型Bean的类型是在相同的,则按名称注入
@Autowired注入时可以针对成员变量或set方法
–通过@Autowired和@Required属性,设置一定要找到匹配的Bean
–使用Qualifier指定注入Bean的名称
@Resource:@AutoWired+@Qualifier
其他注解
@PostContruct:相当于init-method
@PreDestroy:相当于destory-method(只在@scope为singleton时可以使用)
@scope:相当于xml中的scope
xml配置和注解配置混合使用:
xml方式的优点–结构清晰,易于阅读。
注解方式有点–开发边界,属性注入方便。
xml与注解混合开发:
1、引入context命名空间
2、在配置文件中添加context:annotation-condig标签
Spring的AOP
AOP概述
AOP面向切面编程
采用横向抽取机制,取代了纵向继承体系重复性的代码(性能监视、事务管理、安全检查、缓存)。
使用纯java实现,不需要专门的编译过程和类加载器,在运行期间通过代理方式向目标类织入增强代码。
public class UserDaoImpl implement UserDao{
public void save(){
checkPrivilege();
//保存
}
public void update(){
//修改
}
public void delete(){
//删除
}
public void find(){
//查找
}
public void checkPrivilege(){
//权限校验
}
}
纵向继承:
public class BaseDaoImpl {
public void checkPrivilege(){
//权限校验
}
}
public class UserDaoImpl extends BaseDaoImpl{
public void save(){
checkPrivilege();
//保存
}
}
采用横向抽取(代理机制),取代纵向继承:
实现save()的增强。
public class UserDaoImpl implement UserDao{
public void save(){
//保存
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pdGt9Nce-1631933686883)(C:\Users\zhangyn\AppData\Roaming\Typora\typora-user-images\image-20210726162450149.png)]
AOP的相关术语:
Joinpoint(连接点):可以被拦截到的点,spring只支持方法连接点,方法有关的前前后后(抛出异常),都是连接点。
*可以被增强的方法:下述代码的增删改查方法为连接点。
Pointcut(切入点):指的是真正被拦截到的点。
*只想对save方法进行增强,save方法称为切入点。
Adivce(通知):拦截后要做的事情。(方法层)
*对save方法要进行权限校验,权限校验的方法被称为通知。
Introduction(引介):动态在类上添加新的方法和属性。(类层面Spring不考虑)
Target(目标):被增强的对象。
*UserDaoImpl是目标。
Weaving(织入):将Adivce(通知)应用到Target(目标)的过程称为织入。
*将权限校验应用到UserDaoImpl的save方法的过程称为织入。
Proxy(代理):应用了增强后会产生代理对象。
Aspect(切面):切入点和通知的组合。(可由多个切入点和通知组合)
public class UserDaoImpl implement UserDao{
public void save(User user){
checkPrivilege();
//保存
}
public void update(User user){
//修改
}
public void delete(User user){
//删除
}
public void find(){
//查找
}
}
AOP的底层实现(无需手写,可通过配置文件实现)
JDK动态代理(对实现接口的类进行动态代理)
Proxy.newProxyInstance:
第一个参数:类的加载器
第二个参数:类实现的接口
第三个参数:InvocationHandler接口的实例
public class UserDaoImpl implement UserDao{
public void save(User user){
//保存前需要加权限校验
}
public void update(User user){
//修改
}
public void delete(User user){
//删除
}
public void find(){
//查找
}
}
public class MyJdkProxy implements InvocationHandler{
//传入需要增强的类
public UserDao userDao;
public MyJdkProxy(UserDao userDao){
this.userDao=userDao;
}
public Object creatProxy(){
/*newProxyInstance:
第一个参数:类的加载器
第二个参数:类实现的接口
第三个参数:InvocationHandler接口的实例
*/
Object proxy = Proxy.newProxyInstance(userDao.getClass().getClassLoader(),userDao.getClass().getInterfaces(),this)
return proxy;
}
public Object invoke(Object proxy,Method method,Object args[]) throws Throwable{
if ("save".equals(method.getName()){
System.out.println("权限校验");
}
return method.invoke(userDao,args);
}
public class Test{
@Test
public void demo1(){
UserDao userDao = new UserDaoImpl();
UserDao proxy = (UserDao)new MyJdkProxy(userDao).creatProxy();
proxy.save();
}
}
使用CGLIB生成代理(对没有实现接口的类进行动态代理)
1、创建核心类
2、设置父类
3、设置回调
4、生成代理
public class UserDaoImpl implement UserDao{ public void save(User user){ //保存前需要加权限校验 } public void update(User user){ //修改 } public void delete(User user){ //删除 } public void find(){ //查找 }}
public class MyCGLIB implements MethodInterceptor{ //传入需要增强的类 public UserDao userDao; public MyJdkProxy(UserDao userDao){ this.userDao=userDao; } public Object creatProxy(){ //1、创建核心类 Enhancer enhancer = new Enhancer(); //2、设置父类 enhancer.setSupperclass(userDao.getClass()); //3、设置回调 enhancer.setCallback(this); //4、生成代理 Object proxy = enhancer.create(); } public Object intercept(Object proxy,Method method,Object [] args,MethodProxy methodProxy) throws Throwable{ if ("save".equals(method.getName()){ System.out.println("权限校验"); } //返回当前代理类的父类(即UserDao)和参数列表 return methodProcxy.invokeSuper(proxy,args);}
代理知识总结:
-
程序中应优先对接口创建代理,便于程序维护。
-
标记为final的方法不能产生代理。
–JKD动态代理是针对接口生成子类,接口中方法不能用final修饰
–CGLIB是针对目前类产生子类,因此类和方法不能是final
-
spring只能对方法进行增强。
Spring传统AOP
Spring传统AOP的通知类型
前置通知:org.springframework.aop.MethodBeforeAdvice
在目标方法执行前实施增强。
后置通知:org.springframework.aop.AfterReturningAdvice
在目标方法执行后实施增强。
环绕通知:org.springframework.aop.MethodTnterceptor
在目标方法执行前后实施增强。
异常抛出通知:org.springframework.aop.ThrowsAdvice
在目标方法抛出异常后实施增强。
Spring传统AOP的切面类型
Advisor:代表一般切面。Advice本身就是一个切面,对所有目标类所有方法都进行增强。
target,porxyInterfaces,interceptorNames
PointCardAdivaor:代表具有切点的切面。可以指定拦截目标类的某些方法。
–不等待切入点的切面
–带有切入点的切面
Spring的传统AOP自动代理
–基于Bean名称的自动代理
–基于切面信息的自动代理