1.Spring
1.1、简介
轻量级框架,Java EE的春天,当前主流框架!
1.2、优点
-
Spring是一个开源的免费的框架(容器)!
-
Spring是一个轻量级的、非入侵式的框架!
-
控制反转(IOC),面向切面编程(AOP)!
-
支持事务的处理,对框架集合的支持!
总结:Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架
2.IOC理论推导
- 之前程序是主动创建对象,控制权在程序员手上
- 使用set注入后,程序不再具有主动性,而是变成了被动的接收对象!
- 这种思想,从本质上解决了问题,我们程序员不用再去管理对象的创建了。
- 系统的耦合性大大降低~,可以更加专注地在业务的实现上!这是IOC的原型
2.1 控制反转为程序开发带来的好处是什么?**
以前Dao是自己new出来的,编程完全控制在自己手里,通过Spring,初始化过程控制在容器手里,自己不用new它了,反转到容器里了
原来我们的程序,我们控制的是具体的实现,写程序直接写实现。
现在我们控制的是它的接口,它的抽象。原来我们依赖的是它的实现,现在我们依赖的是它的抽象,
从具体的实现反转到抽象的概念上,我们针对的是接口编程,但真正注入进来的是具体的实现
2.2 面向切面编程的核心思想是什么?(AOP)
AOP在java里是利用反射机制实现的
在不需要改变源代码的逻辑前提下,给系统的方法增加一些逻辑进去,并且这个逻辑是在整个程序的生命周期之内都能够应用.
3.IOC创建对象的方式
-
使用无参构造创建对象,默认!
-
假设我们要用有参构建创建对象
-
下标赋值
<!-- 第一种下标赋值!--> <bean id="user" class="com.chen.pojo.User"> <constructor-arg index="0" value="赛飞"/> <constructor-arg index="1" value="男"/> </bean>
-
通过类型创建
<!-- 通过类型创建--> <bean id="user" class="com.chen.pojo.User"> <constructor-arg type="java.lang.String" value="赛贵妃"/> <constructor-arg type="java.lang.String" value="男"/> </bean>
-
通过参数名进行注入
<!-- 通过参数名进行注入--> <bean id="user" class="com.chen.pojo.User"> <constructor-arg name="gender" value="男"/> <constructor-arg name="name" value="赛飞"/> </bean>
总结:在配置文件加载的时候,容器中管理的对象就已经被初始化了
-
4.Spring配置
4.1、别名
如果添加了别名,我们也可以用别名获取到这个对象
<alias name="user" alias="sdas"/>
4.2、Bean的配置
<!-- bean的配置
id: bean 的唯一标识,也就是相当于我们学的对象名
class: bean对象所对应的全限定类名,包名+类型
name: 也是起别名,而且name可以同时起很多个,可用不同的分割(空格,逗号,分号)
-->
<bean id="user" class="com.chen.pojo.User" name="user2 user3,user4;user5">
4.3、import
一般用于团推开发使用,它可以将多个配置文件,,导入合并成一个
假设现在项目中有多个人开发,这三个人负责不同的类开发,不同的类需要注册在不同的bean中,我们可以用import将所有人的bean.xml合并成一个总的。
- 张三
- 李四
- 王五
5.依赖注入
5.1、构造器注入
前面试过了
5.2、Set方式注入【重点】
-
依赖注入:Set注入
- 依赖:bean对象的创建依赖于容器
- 注入:bean对象中的所有属性,由容器来注入!
【环境搭建】
-
复杂类型
-
真实测试对象
<bean id="student" class="com.bdqn.pojo.Student"> <!-- 第一种,普通注入 value--> <property name="name" value="陈赛飞"/> <!-- 第二种,Bean注入 ref--> <property name="address" ref="address"/> <!-- 第三种,数组注入--> <property name="books"> <array> <value>红楼梦</value> <value>西游记</value> <value>水浒传</value> <value>三国演义</value> </array> </property> <!-- 第四种,List注入--> <property name="hobbys"> <list> <value>听歌</value> <value>敲代码</value> <value>打篮球</value> </list> </property> <!-- 第五种,Map注入--> <property name="card"> <map> <entry key="身份证号" value="123456789012345678"/> <entry key="银行卡" value="12344332442"/> <entry key="手机号" value="123456778865"/> </map> </property> <!-- 第六种,Set--> <property name="games"> <set> <value>LOL</value> <value>COC</value> <value>BOB</value> </set> </property> <!-- 第七种,空指针注入--> <property name="wife"> <null/> </property> <!-- 第八种,特殊类型--> <property name="info"> <props> <prop key="学号">2019201293</prop> <prop key="QQ号">324234235</prop> <prop key="性别">男</prop> </props> </property> </bean>
5.3、拓展方式注入
我们可以使用,p命名空间和c命名空间进行注入
官方解释:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-61LxMAeF-1603258955864)(F:\Y2课程\SSM框架\Spring\笔记\img\image-20201016165300104.png)]
测试:
@Test
public void Student(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Student student = (Student) context.getBean("student");
System.out.println(student);
}
注意点:p命名和c命名空间不能直接使用,需要导入xml约束!
- p命名空间的作用:给属性赋值!
- c命名空间的作用:给有参构造函数的参数赋值!
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
6.Bean作用域(scope)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cQ3tIsKb-1603258955870)(F:\Y2课程\SSM框架\Spring\笔记\img\image-20201016191459622.png)]
1.单例模式(默认机制)
singleton
<bean id="user" class="com.chen.pojo.User" name="user2" scope="singleton">
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jYPs6OWU-1603258955873)(F:\Y2课程\SSM框架\Spring\笔记\img\image-20201016191833227.png)]
2.原型模式
prototype 原型,每次从容器中get的时候,都会产生一个新对象
<bean id="user" class="com.chen.pojo.User" name="user2" scope="prototype">
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2s0bS17x-1603258955875)(F:\Y2课程\SSM框架\Spring\笔记\img\image-20201016192132673.png)]
3.其余的
request、session、application只能在web开发中使用到
7.Bean的自动装配
- 自动装配Spring满足bean依赖一种方式!
- Spring会在上下文中自动寻找,并自动改bean装配属性!
在Spring中有三种装配的方式
- 在xml中显示的配置
- 在java中显示配置
- 隐式的自动装配bean
- byName自动装配:根据自己对象的set方法的后面对应的name进行装配
- byType自动装配:根据自己对象属性类型相同的bean进行装配
- byName自动装配:根据自己对象的set方法的后面对应的name进行装配
使用注解实现自动装配
jdk1.5支持的注解,Spring2.5就支持注解了
使用注解须知:
-
导入约束 context约束
-
配置注解配置 context:annotation-config/
<?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" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> </beans>
@Autowired
Spring支持的注解,优先查byType,其次查byName
直接在属性上使用即可,也可以在set方式上使用!
使用Autowired可以不编写set方法,因为它是用映射注入的
属性(required = false) 对象不可以为空 true:可以为空
@Qualifier(value = ‘xx’)
指定自动装配的值,前提是类型相同的前提下
@Resource
java原生注解,优先查byName,其次查byType
属性name:指定配置的name
小结
@Resource和@Autowired的区别:
-
相同点
- 都可以放在属性字段上
-
不同点
- @Resource先通过byName找,找不到名字再用byType类型
- @Autowired先通过byType找,找不到类型再用byName找名字
8.Bean的生命周期
- 生命周期:从创建到销毁的全过程!
Bean的执行顺序:
- 通过配置文件,执行bean 先执行构造方法!
- 赋值时执行set方法
- 初始化前,执行后置处理器中的初始化前的方法Before
- 执行初始化方法
- 初始化后,执行后置处理器中的初始化后的方法after
- 从ioc中获取对象
- 销毁对象执行destroy方法
设置后置处理器后,beans中的所有的bean在有初始化的方法时,都会执行后置处理器中的前后方法
普通类实现BeanPostProcessor接口实现方法!
9.使用注解开发
- 在Spring4之后,要使用注解开发,必须保证AOP包导入了
- 在使用注解需要导入,context约束,增加注解的支持
-
bean
- @Component 组件,放在类上,说明这个类被Spring容器管理了,相等于bean了
-
属性如何注入
- @Value(“xxx”) 注解注入,放在字段上,通过映射的方式去注入,不再用set方法了
-
衍生的注解
- @Component 有几个衍生注解,我们在web开发中,会按照mvc三层分层!
- dao【@Repository】
- service【@Service】
- controller【@Controller】
- 这四个注解功能都是一样的,都是代表将某个类注册到Spring中装配Bean
- @Component 有几个衍生注解,我们在web开发中,会按照mvc三层分层!
-
自动装配
- @Autowired 自动装配通过类型 名字,自动装配,名字如果不能唯一,则
- @Qualifier(value=‘xx’)
- @Nullable 字段标记了这个注解,说明这个字段可以为空
- @Resource 自动通过名字,类型
- @Autowired 自动装配通过类型 名字,自动装配,名字如果不能唯一,则
-
作用域
- @Scope(“singleton”) 单例
- @Scope(“prototype”) 原型
-
小结
-
xml与注解
- xml:更加万能,适用于任何场合!维护简单方便
- 注解:不是自己的类引用不了,维护相对复杂!
最佳实践:
- xml用来管理bean
- 注解只负责完成属性的注入
- 我们在使用的过程中,只需要注意:要想让注解生效,就要开启注解支持和扫描包
-
10.使用java的方式配置Spring
我们现在要完全不使用spring的xml配置了,全权交给java来做!
javaConfig是Spring的一个子项目,在Spring4之后,它成为了核心功能
-
创建一个类作为配置类
- @Configuration 类似于Beans 可以让这个类成为配置类
- @ComponentScan 扫描类
- @Bean 注册,相当于 bean标签
- 其中 方法名 == bean的id属性
- 其次 返回值 == bean的class
-
/** * @Configuration 让这个类成为配置类 类似与Beans * 它也会注册到Spring 容器中,因为它本身也是一个@Component * @ComponentScan 扫描类, * @Bean 注册到bean,相当于我们之前写的一个bean标签 * 方法名字 == bean标签的id属性 * 返回值 == bean标签中的class */ @Configuration![在这里插入图片描述](https://img-blog.csdnimg.cn/20201021134704788.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1RoaXNBYXJvbg==,size_16,color_FFFFFF,t_70#pic_center)
@ComponentScan("com.bdqn.pojo")
@Import(ChenConfig2.class)
public class ChenConfig {
// 注册到bean,相当于我们之前写的一个bean标签
@Bean
public User getUser(){
return new User();
}
}
```
-
创建要实例的类
-
Value(“xx”) 给属性赋值
-
// 让这个类注册到容器里 @Component public class User { public String getName() { return name; } @Value("Aaron and Amder") public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "name='" + name + '\'' + '}'; } private String name; }
-
11.代理模式
为什么要学习代理模式?因为这就是SpringAOP的底层 面试必问【SpringAOP 和 SpringMVC】
- 代理模式的分类:
- 静态代理
- 动态代理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kik0D4pD-1603258955877)(F:\Y2课程\SSM框架\Spring\笔记\img\image-20201019184223032.png)]
11.1、静态代理
-
角色分析:
- 抽象角色:一般会使用接口或者抽象类来解决
- 真实角色:被代理的而角色
- 代理角色:代理真实角色,后我们一般会做附属操作
- 客户:访问代理对象的人
-
代码步骤:
-
接口
public interface Rent { void rent(); }
-
真实对象
public class Host implements Rent{ public void rent() { System.out.println("房东要出租房屋"); } }
-
代理对象
public class Proxy { private Host host; public Proxy() { } public Proxy(Host host) { this.host = host; } public void show(){ host.rent(); System.out.println("中介带客人去看房子"); System.out.println("收中介费"); System.out.println("签合同"); System.out.println("拎包"); } }
-
客户端访问代理对象
public class Client { @Test public void shos(){ Host host = new Host(); Proxy proxy = new Proxy(host); proxy.show(); } }
-
-
代理模式的好处:
- 可以让真实角色的操作更加纯粹!不用去关注公共业务
- 公共业务交给代理角色!实现了业务的分工!
- 公共业务发生扩展的时候,方柏集中管理!
-
缺点
- 一个真实角色就会产生一个代理对象,代码就会翻倍 开发效率会变低
11.2、加深理解
代码步骤
-
接口
public interface UserService { void add(); void del(); void upd(); void sel(); }
-
真实对象
public class UserServiceimpl implements UserService{ public void add() { System.out.println("增加一个用户"); } public void del() { System.out.println("删除一个用户"); } public void upd() { System.out.println("修改一个用户"); } public void sel() { System.out.println("查询一个用户"); } }
-
代理对象
public class UserServiceProxy implements UserService { private UserServiceimpl userServiceimpl; public void setUserServiceimpl(UserServiceimpl userServiceimpl) { this.userServiceimpl = userServiceimpl; } public UserServiceProxy() { } public UserServiceProxy(UserServiceimpl userServiceimpl) { this.userServiceimpl = userServiceimpl; } public void add() { log4j("add"); userServiceimpl.add(); } public void del() { log4j("del"); userServiceimpl.del(); } public void upd() { log4j("upd"); userServiceimpl.upd(); } public void sel() { log4j("sel"); userServiceimpl.sel(); } public void log4j(String msg){ System.out.println("使用了"+msg+"方法"); } }
-
客户测试类
public class Client { @Test public void cli(){ UserServiceimpl serviceimpl = new UserServiceimpl(); UserServiceProxy proxy = new UserServiceProxy(serviceimpl); proxy.add(); } }
关于AOP实现图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DNWLzcOm-1603258955879)(F:\Y2课程\SSM框架\Spring\笔记\img\image-20201019194705749.png)]
11.3 动态代理
- 动态代理和静态代理角色一样,
- 动态代理的类是动态生成的,
- 动态代理分为两大类
- 基于接口的动态代理—JDK 动态代理
- 基于类的动态代理—cglib
- java字节码:javasist
需要了解两个类: Proxy 代理 ,invocationHandier 调用处理程序
- 动态代理的好处
- 可以使真正角色的操作更加纯粹!不用去关注一些公共的业务
- 公共也就是交给代理角色!实现了业务的分工!
- 公共业务发生扩展的时候,方便集中管理!
- 一个动态代理类代理的是一个接口,一般就是对应的是一类业务
- 一个动态代理类可以代理多个类,只要是实现了同一个接口即可!
12.AOP
11.1 什么是AOP
AOP意为:面向切面编程,通过预编译方式和运行期动态代理实现从程序功能的统一维护的一种技术,AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生泛型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合降低,提高程序的可重用性,同时提高开发的效率
在不通过修改源代码的方式,给他添加新功能!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LAeHZWre-1603258955880)(F:\Y2课程\SSM框架\Spring\笔记\img\image-20201021084040174.png)]
11.2 AOP在Spring中的作用
提供声明式事务:允许用户自定义切面
-
横向关注点:跨越应用程序多个模块的方法或功能,即使与我们业务逻辑无关的,但我们需要关注的部分,就是横切关注点。如日志,安全,缓存,事务等等…
-
切面:切面 是通知和切入的结合,切入点:在哪干,通知:干什么
-
通知:切面必须要完成的工作。 它是类中的一个方法
- 前置通知(@Before):方法前执行(一定执行)
- 后置通知(@AfterReturning): 方法后执行,目标方法没有异常 执行;有异常,不执行
- 异常通知(@AfterThrowing):方法发生异常时执行
- 最终通知(@After):相当于finally,,不论是否发生异常都执行
- 环绕通知(@Around): 前置,后置,异常,最终,四大增强方法的结合
-
目标:被通知对象
-
代理:向目标对象应用通知之后创建的对象
-
切入点:实际被增强的方法 要加强的位置
-
连接点:允许使用通知的地方 方法前后都是连接点
-
织入:把切面应用到目标对象来创建新的代理对象的过程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-stnWCDUF-1603258955883)(F:\Y2课程\SSM框架\Spring\笔记\img\image-20201020185637398.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oVoihvDN-1603258955884)(F:\Y2课程\SSM框架\Spring\笔记\img\image-20201020185734796.png)]
12.3 使用Spring实现AOP
【重点】使用AOP织入,需要导入一个依赖包!
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
方式一: 使用Spring的API接口
-
创建接口和真实对象
public interface UserService { void add(); } public class UserServiceimpl implements UserService{ public void add() { System.out.println("增加了一个用户"); } }
-
创建要增强的日志
public class AfterLog4j implements AfterReturningAdvice { // o 返回值 public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable { System.out.println("执行了"+method.getName()+"方法,返回结果为:"+o); } } public class log4j implements MethodBeforeAdvice { // method: 要执行的目标对象的方法 // objects: 参数 // o: 目标对象 public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println(o.getClass().getName()+"的"+method.getName()+"被执行了"); } }
-
配置文件
<!-- 注册bean--> <bean id="userService" class="bdqn.chen.service.UserServiceimpl"/> <bean id="log4j" class="bdqn.chen.log.log4j"/> <bean id="afterLog4j" class="bdqn.chen.log.AfterLog4j"/> <!-- 配置aop:需要导入aop的约束--> <aop:config> <!-- 切入点--> <aop:pointcut id="pointcut1" expression="execution(* bdqn.chen.service.UserServiceimpl.*(..))"/> <!-- 执行环绕--> <aop:advisor advice-ref="log4j" pointcut-ref="pointcut1"/> <aop:advisor advice-ref="afterLog4j" pointcut-ref="pointcut1"/> </aop:config>
方式二:自定义来实现AOP
-
编写增强方法中的增强类
public class Buftter { /** * 前置增强的方法 */ public void buffer(JoinPoint joinPoint){ System.out.println("前置增强"); System.out.println(joinPoint.getSignature().getName()); } /** * 无异常的方法 */ public void afterReturning(){ System.out.println("无异常的方法"); } /** * 异常的方法 */ public void afterthrowing(){ System.out.println("异常的方法"); } /** * 最终通知 */ public void after(){ System.out.println("最终通知"); } /** * 环绕通知 ProceedingJoinPoint point */ public void around(ProceedingJoinPoint point){ try { System.out.println("环绕前置增强..."); Object proceed = point.proceed(); System.out.println("环绕后置增强..."); } catch (Throwable throwable) { throwable.printStackTrace(); } finally { System.out.println("最终通知"); } } }
-
配置切入点,切面,通知
<!--方式二--> <bean id="userService" class="bdqn.chen.service.UserServiceimpl"/> <bean id="buftter" class="bdqn.chen.log.Buftter"/> <aop:config> <!-- 切入点--> <aop:pointcut id="poin" expression="execution(* bdqn.chen.service.UserServiceimpl.*(..))"/> <!-- 执行环绕--> <aop:aspect ref="buftter"> <aop:before method="buffer" pointcut-ref="poin"/> <aop:after-returning method="afterReturning" pointcut-ref="poin"/> <aop:after-throwing method="afterthrowing" pointcut-ref="poin"/> <aop:after method="after" pointcut-ref="poin"/> <aop:around method="around" pointcut-ref="poin"/> </aop:aspect> </aop:config>
方式三:注解实现AOP
-
创建增强类
- @Aspect 标注这个类是一个切面
- @Before(“execution(* bdqn.chen.service.UserServiceimpl.*(…))”) 前置增强
- @AfterReturning(“execution(* bdqn.chen.service.UserServiceimpl.*(…))”) 无异常增强
- @AfterThrowing(“execution(* bdqn.chen.service.UserServiceimpl.*(…))”) 出现异常时增强
- @After(“execution(* bdqn.chen.service.UserServiceimpl.*(…))”) 执行方法后增强
- @Around(“execution(* bdqn.chen.service.UserServiceimpl.*(…))”) 环绕增强
- 同时存在时执行的顺序
- @Around(环绕前)
- @Before(执行方法前)
- pjp.proceed() (执行方法)
- @Around(环绕后)
- @After(执行方法后)
- @AfterReturning/@AfterThrowing(执行有异常增强方法/执行无异常增强方法)
@Aspect // 标注这个类是一个切面 public class AnnotationPoinCut { @Before("execution(* bdqn.chen.service.UserServiceimpl.*(..))") // 切入点 public void before(JoinPoint joinPoint){ System.out.println("执行"+joinPoint.getSignature().getName()+"方法前"); } @AfterReturning("execution(* bdqn.chen.service.UserServiceimpl.*(..))") public void afterReturning(){ System.out.println("无异常执行"); } @AfterThrowing("execution(* bdqn.chen.service.UserServiceimpl.*(..))") public void afterThrowing(){ System.out.println("出异常执行"); } @After("execution(* bdqn.chen.service.UserServiceimpl.*(..))") public void after(){ System.out.println("最终执行方法"); } @Around("execution(* bdqn.chen.service.UserServiceimpl.*(..))") public void around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("环绕前"); Object proceed = pjp.proceed(); System.out.println("环绕后"); System.out.println(pjp.getSignature().getName()); System.out.println(pjp.getSignature().getDeclaringType()); System.out.println(pjp.getSignature().getDeclaringTypeName()); System.out.println(pjp.getSignature()); } }
-
配置中注册增强类
<!-- 方式三--> <bean id="annotationPoinCut" class="bdqn.chen.diy.AnnotationPoinCut"/> <!-- 开启注解支持!JDK(默认 proxy-target-class="false") cglib(proxy-target-class="false")--> <aop:aspectj-autoproxy proxy-target-class="true"/>
Joinpoint对象的使用
/**
* JoinPoint 对象做参数即可使用JoinPoint对象
*/
public void buffer(JoinPoint joinPoint){
System.out.println("前置增强");
System.out.println(joinPoint.getSignature().getName());
}