Spring框架基础(1)

一、核心组件:(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 : 提供了对JUnitTestNG****测试的支持

二、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**(keyvalue,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();
    }
}
相关推荐
用户214118326360239 分钟前
AI 驱动开发:20 分钟搞定智能发票申请单系统
后端
G探险者1 小时前
Java 中 null 值在 JSON 输出时丢失的坑:一次 Object 参数 + Fastjson 多态的血泪教训
后端
振鹏Dong1 小时前
微服务架构及常见微服务技术栈
java·后端
丶小鱼丶1 小时前
二叉树算法之【中序遍历】
java·算法
程序员爱钓鱼2 小时前
Go语言实战案例:简易JSON数据返回
后端·go·trae
程序员爱钓鱼2 小时前
Go语言实战案例:用net/http构建一个RESTful API
后端·go·trae
bobz9652 小时前
firewalld 添加 nat 转发
后端
摇滚侠2 小时前
Oracle 关闭 impdp任务
java
编程爱好者熊浪3 小时前
RedisBloom使用
java