Spring5框架 笔记总结

Posted IT_Holmes

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring5框架 笔记总结相关的知识,希望对你有一定的参考价值。

1. 使用Java 的方式配置Spring


我们不完全使用Spring的xml来配置,我们使用Java代码的方式来操作。

JavaConfig是Spring的一个子项目,在Spring 4之后,他成为了一个核心功能!


通过 ClassPathXmlApplicationContext 和 AnnotationConfigApplicationContext 来获取容器context,对应的情况不同!

前者是通过Spring和xml方式来获取的,后者是通过部分Spring注解和Java代码获取的!

代码如下:

User.java代码(实体类):

package com.itholmes.pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

//此时@Component注解的意思,就是说明这个类被Spring接管了,注册到容器中。
@Component
public class User 

    private String name;

    public String getName() 
        return name;
    

    @Value("itholmes")//属性注入值,也可以在set方法上面。
    public void setName(String name) 
        this.name = name;
    

    @Override
    public String toString() 
        return "User" +
                "name='" + name + '\\'' +
                '';
    

config层下的HolmesConfig.java配置类:

一定记住配置类中几个注解的使用!

package com.itholmes.config;

import com.itholmes.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

//@Configuration注解也会被Spring容器托管,注册到容器中,因为它本身也包含了@Component注解。
//@Configuration代表这是一个配置类,就和我们之前的beans.xml文件是一样的。
@Configuration
@ComponentScan("com.itholmes.pojo")//同样也可以通过@ComponentScan,在这里进行一个扫描操作。
@Import(HolmesConfig2.class)//@Import注解,将相应的配置文件导入当前,也就是合并的效果。
public class HolmesConfig 

    //注册一个bean,就相当于我们之前写的一个bean标签。
    //该方法的名字getUser就是相当于bean标签中的id属性。
    //这个方法的返回值,就相当于bean标签中的class属性。
    @Bean
    public User getUser()
        return new User();//就是返回值,要注入到bean中的对象。
    


模拟导入HolmesConifg配置类的配置类2:

package com.itholmes.config;

import org.springframework.context.annotation.Configuration;

@Configuration
public class HolmesConfig2 


测试类:

一定牢记测试类中的AnnotationConfigApplicationContext(HolmesConfig.class);作用!

/*
    概述:
    @Date:Create in 14:45 2021/12/30
*/

import com.itholmes.config.HolmesConfig;
import com.itholmes.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MyTest 

    public static void main(String[] args) 
        //如果完全使用了配置类方式去做,我们就只能通过AnnotationConfig上下文来获取容器,通过配置类的class对象加载。
        ApplicationContext context = new AnnotationConfigApplicationContext(HolmesConfig.class);
        User user = context.getBean("getUser", User.class);
        System.out.println(user.getName());
    


这种纯java的配置方式,在Spring Boot中随处可见。

2. 代理模式


Java的23种设计模式之一的代理模式,很重要,因为它是Spring SOP的底层内容。

代理模式的分类:

  • 静态代理。
  • 动态代理。

平时,我们学习中操作的都是静态代理模式,定义一个接口,一个用户,一个代理角色,一个厂家。用户通过代理来获取操作厂家提供资源的一个效果。


纵向开发 和 横向开发的效果图:

3. 动态代理 简述


简而言之,动态代理,在代理模式的情况下,它是自动生成代理类。

动态代理的描述:

  • 动态代理的底层都是通过反射来实现的。
  • 动态代理的代理类是动态生成的,不是我们直接写好的!
  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理。
    • 基于接口动态代理机制:JDK动态代理(也是默认的)。
    • 基于类的动态代理机制:cglib动态代理。
    • java字节码实现:Javassist。

Spring AOP是基于动态代理的,基于两种动态代理机制:JDK动态代理(也是默认的)和CGLIB动态代理。


需要了解两个类:Proxy (代理),InvocationHandler (调用处理程序)。

InvocationHandler是java.lang.reflect反射包下的一个接口:

Proxy 是java.lang.reflect反射包下的一个类:

proxy类对应的4个静态方法:

4. 动态代理的使用


Rent接口:

package com.itholmes.demo3;

public interface Rent 
    void rent();

Host类(房东):

package com.itholmes.demo3;

public class Host implements Rent

    @Override
    public void rent() 
        System.out.println("房东要出租房子!");
    


ProxyInvocationHandler.java实现InvocationHandler接口的类,这个类可以自动生成代理类。

package com.itholmes.demo3;


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//我们会用这个类自动生成代理类!
public class ProxyInvocationHandler implements InvocationHandler 

    //被代理的接口
    private Rent rent;

    public void setRent(Rent rent) 
        this.rent = rent;
    

    //生成得到代理类
    //第一个参数,类构造器:this.getClass().getClassLoader()加载到代理类在那个位置。
    //第二个参数,得到实现rent的接口:rent.getClass().getInterfaces()表示它要代理的接口是哪一个接口。
    //第三个参数,实现InvocationHandler的类,这里我们本身就实现了InvocationHandler了,所以用this就可,。
    public Object getProxy()
        Object o = Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
        //System.out.println("判断o:"+o);
        return o;
    

    //上面的Proxy.newProxyInstance()方法执行后,会执行当前的invoke方法来处理代理实例,并返回结果。
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 

        seeHouse();

        //动态代理的本质,就是使用反射机制实现的!
        Object invoke = method.invoke(rent, args);
        //System.out.println("判断invoke:"+invoke);

        fare();

        return invoke;
    

    //我们可以通过打印来判断这里的invoke对象和上面的o对象是一个对象么?
    //invoke:com.itholmes.demo3.Host@2b193f2d
    //getProxy:com.itholmes.demo3.Host@2b193f2d
    //返回结果不难看出就是一个对象。

    public void seeHouse()
        System.out.println("中介带看房子");
    

    public void fare()
        System.out.println("中介收费");
    

Client用户测试类:

package com.itholmes.demo3;

public class Client 

    public static void main(String[] args) 
        //真实角色(房主)
        Host host = new Host();

        //代理角色(中介):现在没有
        ProxyInvocationHandler handler = new ProxyInvocationHandler();
        //通过调用程序处理角色(当前我们定义的ProxyInvocationHandler类)来处理我们要调用的接口对象。
        handler.setRent(host);
        Rent proxy = (Rent)handler.getProxy();//这里的proxy就是动态生成的!
        proxy.rent();
        
    


此外,invoke中的method方法可以调用很多方法,例如:
method.getName()获取名字等等。

5. Spring AOP


面向对象程序设计(Object Oriented Programming , OOP)

面向切面编程:(Aspect Oriented Programming , AOP)

AOP是OOP的延续。


AOP的一个效果图如下:


SpringAOP中,通过Advice定义横切逻辑。

在Spring中支持5种类型的Advice:

6. 实现aop(一) 通过SpringAPI接口实现(围绕接口来实现)


实现AOP前,需要一个织入依赖包aspectjweaver!

导入完包后,要导入响应的约束和支持约束条件:

其实不管什么约束条件都是差不多的格式,稍微改改字母就能修改。

xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
            http://www.springframework.org/schema/aop
            https://www.springframework.org/schema/aop/spring-aop.xsd"

上面准备工作完成后,我们创建两个类分别实现了AfterReturningAdvice 和 MethodBeforeAdvice接口:

log类实现MethodBeforeAdvice方法执行前调用它:

package com.itholmes.log;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class Log implements MethodBeforeAdvice

    //before方法的参数:
    //method:要执行的目标对象的方法
    //objects:参数
    //o:目标对象
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable 
        System.out.println(o.getClass().getName()+"的"+method.getName()+"被执行了");
    

AfterLog类,调用AfterReturningAdvice接口,负责执行方法执行后的内容:

package com.itholmes.log;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

public class AfterLog implements AfterReturningAdvice 

    //returnValue:使用AfterReturningAdvice是可以拿到一个returnValue返回值的。
    //其他的参数和BeforeAdvice中的before参数一样。
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] objects, Object o1) throws Throwable 
        System.out.println("执行了"+method.getName()+"方法,返回结构为:"+returnValue);
    

UserService接口:

package com.itholmes.service;

public interface UserService 
    public void add();
    public void delete();
    public void update();
    public void select();

UserServiceImpl实现类:

package com.itholmes.service;

public class UserServiceImpl implements UserService

    @Override
    public void add() 
        System.out.println("增加了一个用户");
    

    @Override
    public void delete() 
        System.out.println("删除了一个用户");
    

    @Override
    public void update() 
        System.out.println("更新了一个用户");
    

    @Override
    public void select() 
        System.out.println("查询了一个用户");
    

再往后,先在xml文件中,注册bean,然后通过xml方式来指定aop的相关内容了。
创建applicationContext.xml文件:

<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/aop
            https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--注册bean-->
    <bean id="userService" class="com.itholmes.service.UserServiceImpl"/>
    <bean id="log" class="com.itholmes.log.Log"/>
    <bean id="afterLog" class="com.itholmes.log.AfterLog"/>

    <!--上面的AOP约束一定要导入!-->
    <!--配置AOP:需要导入aop的约束-->
    <!--方式一:使用原生的Spring API接口-->
    <aop:config>

        <!--切入点: expression:表达式 , execution(要执行的位置! * * * *) *在这代表的是所有的意思。-->
        <!--execution()方法参数含义下面会说到!-->
        <aop:pointcut id="pointcut" expression="execution(* com.itholmes.service.UserServiceImpl.*(..))"></aop:pointcut>

        <!--执行环绕增加-->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>

    </aop:config>

</beans>

最后,我们测试一下mytest类:

import com.itholmes.service.UserService;
import com.itholmes.service.UserServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest 
    public static void main(String[] args) 
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //注意:AOP实现原理是动态代理,而动态代理代理的是接口,因此在这我们只能写接口类型。
        UserService userService = (UserService) context.getBean("userService");
        userService.add();
    

7. execution()方法参数含义


先说一下execution()方法参数的含义。

execution()是最常用的切点函数,其语法如下所示:

整个表达式可以分为五个部分:

  1. execution(): 表达式主体。
  2. 第一个*号:表示返回类型,*号表示所有的类型。
  3. 包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。
  4. 第二个*号:表示类名,*号表示所有的类。
  5. *(..):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。

这些参数理解了才更容易理解AOP。

8. 实现aop(二) 自定义类方式 (围绕自定义类,也就是切面(aspect)定义来实现)


测试类,接口,接口实现类不变。

首先,创建一个自定义类:

package com.itholmes.diy;

public class DiyPointCut 

    public void before()
        System.out.println("------方法执行前-------");
    

    public void after()
        System.out.println("-------方法执行后--------");
    


xml配置文件来进行配置:
其实就是换了aop:aspect来操作而已:

<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/aop
            https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--注册bean-->
    <bean id="userService" class="com.itholmes.service.UserServiceImpl"/>
    <bean id="log" class="com.itholmes.log.Log"/>
    <bean id="afterLog" class="com.itholmes.log.AfterLog"/>

    <!--方式二:通过自定义类方式实现-->
    <bean id="diy" class="com.itholmes.diy.DiyPointCut"/>
    <aop:config>

        <!--自定义切面: ref指定要引用的类-->
        <aop:aspect ref="diy"Spring5框架 笔记总结

Spring5框架 笔记总结

Spring5框架 笔记总结

Spring | Spring5学习笔记 #yyds干货盘点#

尚硅谷Spring学习笔记-- Spring5新功能

Spring5课堂笔记