轻量级框架,Java EE的春天,当前主流框架!
Spring是一个开源的免费的框架(容器)!
Spring是一个轻量级的、非入侵式的框架!
控制反转(IOC),面向切面编程(AOP)!
支持事务的处理,对框架集合的支持!
总结:Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架
以前Dao是自己new出来的,编程完全控制在自己手里,通过Spring,初始化过程控制在容器手里,自己不用new它了,反转到容器里了
原来我们的程序,我们控制的是具体的实现,写程序直接写实现。
现在我们控制的是它的接口,它的抽象。原来我们依赖的是它的实现,现在我们依赖的是它的抽象,
从具体的实现反转到抽象的概念上,我们针对的是接口编程,但真正注入进来的是具体的实现
AOP在java里是利用反射机制实现的
在不需要改变源代码的逻辑前提下,给系统的方法增加一些逻辑进去,并且这个逻辑是在整个程序的生命周期之内都能够应用.
使用无参构造创建对象,默认!
假设我们要用有参构建创建对象
下标赋值
<!-- 第一种下标赋值!--> <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>总结:在配置文件加载的时候,容器中管理的对象就已经被初始化了
如果添加了别名,我们也可以用别名获取到这个对象
<alias name="user" alias="sdas"/> 一般用于团推开发使用,它可以将多个配置文件,,导入合并成一个
假设现在项目中有多个人开发,这三个人负责不同的类开发,不同的类需要注册在不同的bean中,我们可以用import将所有人的bean.xml合并成一个总的。
张三李四王五 前面试过了
依赖注入: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> 我们可以使用,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" [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cQ3tIsKb-1603258955870)(F:\Y2课程\SSM框架\Spring\笔记\img\image-20201016191459622.png)]
singleton
<bean id="user" class="com.chen.pojo.User" name="user2" scope="singleton">[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jYPs6OWU-1603258955873)(F:\Y2课程\SSM框架\Spring\笔记\img\image-20201016191833227.png)]
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)]
request、session、application只能在web开发中使用到
在Spring中有三种装配的方式
在xml中显示的配置在java中显示配置隐式的自动装配bean byName自动装配:根据自己对象的set方法的后面对应的name进行装配 byType自动装配:根据自己对象属性类型相同的bean进行装配 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> Spring支持的注解,优先查byType,其次查byName
直接在属性上使用即可,也可以在set方式上使用!
使用Autowired可以不编写set方法,因为它是用映射注入的
属性(required = false) 对象不可以为空 true:可以为空
@Qualifier(value = ‘xx’)
指定自动装配的值,前提是类型相同的前提下
java原生注解,优先查byName,其次查byType
属性name:指定配置的name
@Resource和@Autowired的区别:
相同点
都可以放在属性字段上不同点
@Resource先通过byName找,找不到名字再用byType类型@Autowired先通过byType找,找不到类型再用byName找名字Bean的执行顺序:
通过配置文件,执行bean 先执行构造方法!赋值时执行set方法初始化前,执行后置处理器中的初始化前的方法Before执行初始化方法初始化后,执行后置处理器中的初始化后的方法after从ioc中获取对象销毁对象执行destroy方法 设置后置处理器后,beans中的所有的bean在有初始化的方法时,都会执行后置处理器中的前后方法
普通类实现BeanPostProcessor接口实现方法!
bean
@Component 组件,放在类上,说明这个类被Spring容器管理了,相等于bean了属性如何注入
@Value(“xxx”) 注解注入,放在字段上,通过映射的方式去注入,不再用set方法了衍生的注解
@Component 有几个衍生注解,我们在web开发中,会按照mvc三层分层! dao【@Repository】service【@Service】controller【@Controller】 这四个注解功能都是一样的,都是代表将某个类注册到Spring中装配Bean自动装配
@Autowired 自动装配通过类型 名字,自动装配,名字如果不能唯一,则 @Qualifier(value=‘xx’) @Nullable 字段标记了这个注解,说明这个字段可以为空@Resource 自动通过名字,类型作用域
@Scope(“singleton”) 单例@Scope(“prototype”) 原型小结
xml与注解
xml:更加万能,适用于任何场合!维护简单方便注解:不是自己的类引用不了,维护相对复杂!最佳实践:
xml用来管理bean注解只负责完成属性的注入我们在使用的过程中,只需要注意:要想让注解生效,就要开启注解支持和扫描包 我们现在要完全不使用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@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; } 为什么要学习代理模式?因为这就是SpringAOP的底层 面试必问【SpringAOP 和 SpringMVC】
代理模式的分类: 静态代理动态代理[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kik0D4pD-1603258955877)(F:\Y2课程\SSM框架\Spring\笔记\img\image-20201019184223032.png)]
角色分析:
抽象角色:一般会使用接口或者抽象类来解决真实角色:被代理的而角色代理角色:代理真实角色,后我们一般会做附属操作客户:访问代理对象的人代码步骤:
接口
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(); } }代理模式的好处:
可以让真实角色的操作更加纯粹!不用去关注公共业务公共业务交给代理角色!实现了业务的分工!公共业务发生扩展的时候,方柏集中管理!缺点
一个真实角色就会产生一个代理对象,代码就会翻倍 开发效率会变低 代码步骤
接口
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(); } }[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DNWLzcOm-1603258955879)(F:\Y2课程\SSM框架\Spring\笔记\img\image-20201019194705749.png)]
需要了解两个类: Proxy 代理 ,invocationHandier 调用处理程序
动态代理的好处 可以使真正角色的操作更加纯粹!不用去关注一些公共的业务公共也就是交给代理角色!实现了业务的分工!公共业务发生扩展的时候,方便集中管理!一个动态代理类代理的是一个接口,一般就是对应的是一类业务一个动态代理类可以代理多个类,只要是实现了同一个接口即可! AOP意为:面向切面编程,通过预编译方式和运行期动态代理实现从程序功能的统一维护的一种技术,AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生泛型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合降低,提高程序的可重用性,同时提高开发的效率
在不通过修改源代码的方式,给他添加新功能!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LAeHZWre-1603258955880)(F:\Y2课程\SSM框架\Spring\笔记\img\image-20201021084040174.png)]
提供声明式事务:允许用户自定义切面
横向关注点:跨越应用程序多个模块的方法或功能,即使与我们业务逻辑无关的,但我们需要关注的部分,就是横切关注点。如日志,安全,缓存,事务等等…
切面:切面 是通知和切入的结合,切入点:在哪干,通知:干什么
通知:切面必须要完成的工作。 它是类中的一个方法
前置通知(@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)]
【重点】使用AOP织入,需要导入一个依赖包!
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.6</version> </dependency>创建接口和真实对象
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>编写增强方法中的增强类
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> 创建增强类
@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"/>