一、核心组件:(1)里主要有AOP Beans
Spring Core**:基础。Spring 其他所有的功能都需要依赖于该类库。主要提供 IoC****依赖注入功
能。
Spring Aspects: 该模块为与AspectJ的集成提供支持。
Spring AOP:提供了面向切面的编程实现**。
Spring JDBC : Java数据库连接。
Spring JMS:Java消息服务。
Spring ORM : 用于支持Hibernate等**ORM工具**。
Spring Web : 为创建****Web应用程序提供支持。
Spring Test : 提供了对JUnit和TestNG****测试的支持
二、Spring基本概念
2.1、Spring是什么?
Spring是一个分层的轻量级的开源Java框架
spring有两大核心内容 IoC:控制反转 AOP:面向切面
2.2、IoC控制反转
以前买冰棍 去超市 付款 买东西
现在买东西 手机上声明需要XX商品 外卖送达
IoC**(Inverse of Control:控制反转)是一种****==设计思想==**,就是 ==将原本在程序中手动创建对象的控制权,
交由Spring框架来管理。****== IoC 在其他语言中也有应用,并非 Spring 特有。 ==IoC****容器是
Spring用来实现IoC的载体,IoC容器实际上就是个Map**(key,value),Map中存放的是各种对象。==将对象之间的相互依赖关系交给IoC容器来管理,并由IoC容器完成对象的注入。**这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。 **IoC容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件xml/**注解即可,完全不用考虑对象是如何被创建出来的。
优点
实现组件之间的解耦
提高程序的灵活性和可维护性
缺点
对程序员来说创建对象的步骤变复杂了,不直观
因为使用反射来创建对象,所以在效率上会有些损耗。但相对于程序的灵活性和可维护性来
说,这点损耗是微不足道的。
缺少IDE重构的支持,如果修改了类名,还需到XML文件中手动修改,
2.2.1、将类加入IOC容器进行管理
@Controller @RestController 访问层
@Service 业务层
@Repository 数据访问层
@Component 其他组件
@Configuration+@Bean 声明方法将方法中返回的对象纳入IoC容器
2.2.2、service层
访问层关心获取参数,响应数据
不关心业务处理,所有的业务处理都要service层完成
在controller中需要调用service中的方法,
需要再controller中获得service的对象
2.2.3、DI 依赖注入 Bean的两个装配注解 可实现IoC
通过依赖关系注入 主要目的是解耦合
有两个注解可以实现依赖注入
@Autowired
@Resource
@Autowired 是通过bytype的方式寻找bean
如果容器中出现两个bean都符合类型需求,就会报错
如果需要通过byname的方式查找对象需要使用@Qualifier("lista")
@Resource默认使用ByName的方式查找对象,如果找不到会自动切换为bytype的方式
六、Bean!! 包括前面两个
Spring Bean是Spring框架管理的Java对象,由Spring IoC容器实例化、组装和管理。Bean是Spring应用程序的基础组件,通过依赖注入(DI)机制相互协作。
6.1、声明Bean的注解
@Component
-
作用:通用的Bean声明注解
-
范围:标记任何需要被Spring管理的组件
派生注解(特殊化的@Component)
1 我们一般使用 @Autowired 注解自动装配 bean,要想把类标识成可用于 @Autowired 注解自动装配的
bean 的类,采用以下注解可实现:
2 @Component :通用的注解,可标注任意类为 Spring 组件。如果一个Bean不知道属于哪个层,
可以使用 @Component 注解标注。
3 @Repository : 对应持久层即 Dao 层,主要用于数据库相关操作。
4 @Service : 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao层。
5 @Controller : 对应 Spring MVC 控制层,主要用户接受用户请求并调用 Service 层返回数据给前
端页面。
6 @Configuration 配置类
6.2 SpringBean的生命周期

1)根据配置情况调用 Bean 构造方法或工厂方法实例化 Bean。
2)利用依赖注入完成 Bean 中所有属性值的配置注入。
3)如果Bean实现了特定的Aware接口(例如BeanNameAware、BeanFactoryAware、ApplicationContextAware等),Spring会在Bean的初始化过程中调用这些接口定义的方法,使Bean能够感知到容器的相关属性和功能。
如果 Bean 实现了 BeanNameAware 接口,则 Spring 调用 Bean 的 setBeanName() 方法传入当前 Bean 的 id 值。
4)如果 Bean 实现了 BeanFactoryAware 接口,则 Spring 调用 setBeanFactory() 方法传入当前工厂实例的引用。
5)如果 Bean 实现了 ApplicationContextAware 接口,则 Spring 调用 setApplicationContext() 方法传入当前 ApplicationContext 实例的引用。
6)如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的预初始化方法 postProcessBeforeInitialzation() 对 Bean 进行加工操作,此处非常重要,Spring 的 AOP 就是利用它实现的。
7)如果 Bean 实现了 InitializingBean 接口,则 Spring 将调用 afterPropertiesSet() 方法。
8)如果在配置文件中通过 init-method 属性指定了初始化方法,则调用该初始化方法。
9)如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的初始化方法 postProcessAfterInitialization()。此时,Bean 已经可以被应用系统使用了。
10)如果在 <bean> 中指定了该 Bean 的作用范围为 scope="singleton",则将该 Bean 放入 Spring IoC 的缓存池中,将触发 Spring 对该 Bean 的生命周期管理;如果在 <bean> 中指定了该 Bean 的作用范围为 scope="prototype",则将该 Bean 交给调用者,调用者管理该 Bean 的生命周期,Spring 不再管理该 Bean。
11)如果 Bean 实现了 DisposableBean 接口,则 Spring 会调用 destory() 方法将 Spring 中的 Bean 销毁;如果在配置文件中通过 destory-method 属性指定了 Bean 的销毁方法,则 Spring 将调用该方法对 Bean 进行销毁。
6.3、 bean的练习代码
java
package com.easy.component;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component("easybeanaa")
public class EasyBean implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean {
public EasyBean(){
System.out.println("-----EasyBean构造方法");
}
@Override
public void setBeanName(String name) {
System.out.println("执行了setBeanName,该类的对象在IoC容器中叫做:"+name);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
//beanFactory.getBean("easybeanaa");
//System.out.println(beanFactory.getBean("list"));
System.out.println("执行了setBeanFactory--,使用"+beanFactory+"工厂创建实例对象");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("执行了setApplicationContext方法");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("----------afterPropertiesSet");
}
public void initEasyBean(){
System.out.println("--------initEasyBean Method");
}
}
6.4、Spring Bean的作用域注解 @Scope
1 @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) 默认作用域为单例模式 整个过程中使用一个对象
2 @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) 原型模式 每次DI注入一个新的对象
3 WebApplicationContext 针对web应用
4 SCOPE_REQUEST 每一次请求创建一个新对象\
request: 每一次HTTP****请求都会产生一个新的bean,该bean仅在当前HTTP request内有效。
5 SCOPE_SESSION 每一会话创建一个新对象
session : 每一次HTTP****请求都会产生一个新的 bean,该bean仅在当前 HTTP session内有效。
6 SCOPE_APPLICATION 每个应用都要使用一个对象
7 prototype : 每次请求都会创建一个新的 bean 实例。
8 global-session: 全局session作用域,仅仅在基于portlet的web应用中才有意义,Spring5已经
没有了。Portlet是能够生成语义代码(例如:HTML)片段的小型Java Web插件。它们基于portlet容
器,可以像servlet一样处理HTTP请求。但是,与 servlet 不同,每个 portlet 都有不同的会话。
三、AOP:面向切面
3.0 基本理解和作用
AOP (Aspect Orient Programming),直译过来就是 面向切面编程,AOP 是一种编程思想,是面向对象编程(OOP)的一种补充
作用:AOP可以拦截指定的方法并且对方法增强,而且无需侵入到业务代码中,使业务与非业务处理逻辑分离,比如Spring的事务,通过事务的注解配置,Spring会自动在业务方法中开启、提交事务,并且在事务处理失败时,执行相应的回滚策略。
1 所有的方法 连接点
2 真实切入的方法 切入点
3 业务增强代码 通知
4 被代理的 代理的目标对象 目标
5 生成的代理对象 代理
6 切点和通知的结合 切面
7 将通知融入到切点中的过程(将增强代码应用到目标上,生成代理对象) 织入(Weaving 植入)

3.1 Spring AOP是基于==动态代理==实现****的,
JDK****的动态代理:如果要代理的对象,实现了某个接口,那么Spring AOP会使用JDK Proxy,去创 建代理对象,
CGLIB****的动态代理:对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候
Spring AOP会使用 Cglib生成一个被代理对象的子类来作为代理。注意, CGLIB 是通过继承的方
式做的动态代理,因此如果某个 类被标记为 final ,那么它是无法使用 CGLIB 做动态代理的。

3.2 AOP编程术语
==切面(Aspect)==:**切面泛指交叉业务逻辑。**上例中的事务处理、日志处理就可以理解为切
面。常用的切面是通知(Advice)。实际就是对主业务逻辑的一种增强。
连接点(JoinPoint):连接点指可以被切面织入的具体方法。通常业务接口中的方法均为连接
点。
==切入点(Pointcut)==:**切入点指声明的一个或多个连接点的集合。**通过切入点指定一组方
法。 被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增
强的。
目标对象(Target):**目标对象指将要被增强的对象 。 即包含主业务逻辑的类的对象。**上例中的
StudentServiceImpl 的对象若被增强,则该类称为目标类,该类对象称为目标对象。当然, 不被
增强,也就无所谓目标不目标了。
==通知(Advice)==:通知表示切面的执行时间,Advice 也叫增强。上例中的
MyInvocationHandler 就可以理 解为是一种通知。换个角度来说,通知定义了增强代码切入到目
标代码的时间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。
AspectJ 中常用的通知有五种类型:前置通知、后置通知、环绕通知、异常通知、最终通知

3.3、 AOP的通知类型

通知运行顺序代码练习:
java
package com.easy.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class EasyEController {
@RequestMapping("teste")
public String teste(){
System.out.println("-----teste");
return "test method";
}
}
package com.easy.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class EasyAspect {
@Pointcut("execution(public String com.easy.controller.EasyEController.teste())")//目标切入点
public void pointcutA(){}
@Before(value="pointcutA()")
public void before(){
System.out.println("-------前置通知");
}
@After(value="pointcutA()")
public void after(){
System.out.println("-----后置通知");
}
@AfterReturning(value="pointcutA()")
public void returning(){
System.out.println("-----返回通知");
}
@AfterThrowing(value="pointcutA()")
public void throwing(){
System.out.println("--------抛出异常通知");
}
@Around(value = "pointcutA()")
public void around(ProceedingJoinPoint point){
System.out.println("-------around前置");
try{
point.proceed();//链接点执行 被代理对象执行业务
System.out.println("---------around 返回");
}catch (Throwable e){
e.printStackTrace();
System.out.println("---------around");
}finally {
System.out.println("---------around 后置");
}
}
}
四、Spring的优点?(使用Spring框架的好处)
3.1 轻量:
Spring 是轻量的,基本的版本大约2MB
3.2 针对接口编程,解耦合:
Spring 提供了 Ioc 控制反转,由容器管理对象,对象的依赖关系。原来在
程序代码中的对象创建方式,现在由容器完成。对象之间的依赖解耦合。
3.3 面向切面的编程(AOP):
Spring支持面向切面的编程,并且把应用业务逻辑和系统服务分开
方便集成各种优秀框架:如MyBatis等
五、AOP动态代理和静态代理
特性 | 静态代理 | 动态代理 |
---|---|---|
创建时机 | 编译期 | 运行时 |
代码量 | 需要为每个被代理类编写代理类 | 一个处理器可代理多个类 |
灵活性 | 低,修改需要重新编译 | 高,可以动态调整代理逻辑 |
性能 | 较好,直接调用 | 稍差,涉及反射调用 |
适用场景 | 简单、固定的代理需求 | 复杂、多变的代理需求 |
实现方式 | 手动编码 | JDK Proxy/CGLIB等 |
代理目标 | 类和接口 | JDK代理仅接口,CGLIB可代理类 |
5.1、动态代理 只有运行时才能确定类的类型
5.1.1、JDK动态代理:
作用同AOP:不修改原有的代码的情况下对已有的代码进行增强
动态:在运行阶段才创建出类型和对象
JDKProxy生成的代理类是实现了被代理类的接口
java
package com.easy.proxy.jdkproxy;
public interface JdkService {
void doJdkService();
}
package com.easy.proxy.jdkproxy;
public class EasyJdkService implements JdkService{
@Override
public void doJdkService() {
System.out.println("-----------JDKService-----------");
}
}
package com.easy.proxy.jdkproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class EasyInvocationHandler implements InvocationHandler {
//声明被代理对象
private Object proxyedObj;
public EasyInvocationHandler(Object proxyedObj){
this.proxyedObj = proxyedObj;
}
//代理需要执行的代码 invoke里的(必需) 返回值类型 Object代表被代理对象要返回的类型
//method 就是调用的方法 args调用方法是传递的参数
//invoke 执行
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("-------------------------前置执行的代码");
Object result = method.invoke(proxyedObj,args);//被代理对象真实执行业务
System.out.println("-------------------------后置执行的代码");
return result;
}
}
package com.easy.proxy.jdkproxy;
import java.lang.reflect.Proxy;
public class JDKProxyTest {
//获得一个代理对象的实例 依赖于接口
static Object getProxy(Object proxyed){
return Proxy.newProxyInstance(JDKProxyTest.class.getClassLoader()
,proxyed.getClass().getInterfaces(),new EasyInvocationHandler(proxyed));
}
//代理类实例 获得被代理对象的所有接口 被代理的的业务有代理类真实实现
//JDKProxy生成的代理类是实现了被代理类的接口
//CGLIB通过子类继承父类实现动态代理
public static void main(String[] args) {
Object proxy=getProxy(new EasyJdkService());
System.out.println(proxy.getClass());//运行时才能确定类型
JdkService jdkProxy=(JdkService) proxy;
jdkProxy.doJdkService();
}
}
5.1.2、CGLib动态代理
CGLib 通过子类继承父类实现动态代理 跑步时生了个孩子 原来的是父类
java
package com.easy.proxy.cgilb;
public class CGService {
public void testa(){
System.out.println("---------testa");
}
public String testb(String str){
System.out.println("testb");
return "hello"+str;
}
public String testc(){
int a=12/0;
return null;
}
}
package com.easy.proxy.cgilb;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
//第三方jar包
public class CGIntercepter implements MethodInterceptor {
//被代理对象
Object proxy;
CGIntercepter(Object obj){
proxy=obj;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("---------1前置代码");
//执行被代理对象的方法
Object result = null;
try {
//System.out.println(o.getClass());
//System.out.println(method);
//System.out.println(methodProxy);
result = methodProxy.invoke(proxy, objects);
System.out.println("---------2正常完成后执行的代码");
}catch (Exception e){
e.printStackTrace();
System.out.println("------------出现异常");
}finally {
System.out.println("------------总会执行的代码");
}
return result;
}
}
package com.easy.proxy.cgilb;
import net.sf.cglib.proxy.Enhancer;
public class CGTest {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(CGService.class);//要代理的类
enhancer.setCallback(new CGIntercepter(new CGService()));
CGService ser=(CGService) enhancer.create();//创建代理类,创建代理对象
ser.testa();
}
}
5.2、静态代理 编译时已经确定类的类型\
不修改原有代码的基础上对业务进行增强
静态:原本就有的类型 原本就有的对象
EasyProxy代理EasyService
java
package com.easy.proxy.staticproxy;
public interface EasyService {
void doService();
}
package com.easy.proxy.staticproxy;
public class EasyBean implements EasyService{
@Override
public void doService() {
System.out.println("-----业务处理");
}
}
package com.easy.proxy.staticproxy;
public class EasyProxy implements EasyService{
EasyService easyService;
public EasyProxy(EasyService easyService){
this.easyService=easyService;
}
@Override
public void doService() {
System.out.println("---------代理方法---业务执行前");
easyService.doService();
System.out.println("---------代理方法---业务执行后");
}
}
package com.easy.proxy.staticproxy;
public class EasyTest {
public static void main(String[] args) {
EasyService easybean=new EasyBean();
EasyService easyService=new EasyProxy(easybean);
//是确定的
easyService.doService();
}
}