Spring5学习 (核心)

Spring5

官方文档:https://docs.spring.io/spring/docs/5.3.0-SNAPSHOT/spring-framework-reference/index.html

zip下载地址:https://repo.spring.io/release/org/springframework/spring/5.2.6.RELEASE/

bean注入:

无参构造 (set注入 多种方式https://docs.spring.io/spring/docs/5.3.0-SNAPSHOT/spring-framework-reference/core.html#beans-setter-injection)

classpath下beans.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 使用Spring创建bean   -->
    <bean id="userDao" class="com.yan.dao.impl.UserDaoImpl">
    </bean>
    <bean id="userDaoOracle" class="com.yan.dao.impl.UserDaoImplOracle">
    </bean>
    <bean id="userService" class="com.yan.service.impl.UserServiceImpl">
        <!--要有set方法-->
        <property name="userDao" ref="userDaoOracle">
        </property>
    </bean>
</beans>

使用:

		ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        UserService userService = (UserService)context.getBean("userService");
        userService.getUser();
有参构造 (构造器注入)

下标

	<bean id="hello" class="com.yan.entity.Hello">
        <constructor-arg index="0" value="str000">
        </constructor-arg>
        <constructor-arg index="1" value="1">
        </constructor-arg>
    </bean>

类型

<bean id="hello" class="com.yan.entity.Hello">
        <constructor-arg type="java.lang.String" value="str000">
        </constructor-arg>
        <constructor-arg type="java.lang.Integer" value="1">
        </constructor-arg>
    </bean>

参数名

<!-- 参数名   -->
    <bean id="hello" class="com.yan.entity.Hello">
        <constructor-arg name="str" value="str000">
        </constructor-arg>
        <constructor-arg name="num" value="1">
        </constructor-arg>
    </bean>

bean引用

 <bean id="str" class="java.lang.String">
        <constructor-arg type="java.lang.String" value="str000">
        </constructor-arg>
    </bean>
    <bean id="num" class="java.lang.Integer">
        <constructor-arg type="int" value="1">
        </constructor-arg>
    </bean>
    <bean id="hello" class="com.yan.entity.Hello">
        <constructor-arg name="str" ref="str">
        </constructor-arg>
        <constructor-arg name="num" ref="num">
        </constructor-arg>
    </bean>

构造函数声明:

public Hello(String str,Integer num)

合并配置文件

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

bean作用域

配置方法:bean的scope属性

 <bean id="hello" class="com.yan.entity.Hello" scope="prototype">
        <constructor-arg name="str" ref="str">
        </constructor-arg>
        <constructor-arg name="num" ref="num">
        </constructor-arg>
    </bean>

可选值:

singleton 单例(默认)

prototype 多例

request

session

application

websocket

自动装配bean

xml (byName,byType)

byName:要求id全局唯一并且和要注入的属性的变量名一致

byType: 要求此类型实例全局唯一并且和要注入的属性类型一致

	<bean id="dog" class="com.yan.entity.Dog">
    </bean>
    <bean id="cat" class="com.yan.entity.Cat">
    </bean>
    <bean id="people" class="com.yan.entity.People" autowire="byName">
    </bean>

java代码

注解

额外配置:

导入context命名空间并且开启注解支持:

<?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>

demo

xml配置:

	<context:annotation-config/>
    <bean id="dog" class="com.yan.entity.Dog">
    </bean>
    <bean id="cat" class="com.yan.entity.Cat">
    </bean>
    <bean id="people" class="com.yan.entity.People">
	</bean>

代码中配置(有注入注解可以没用set方法):

public class People {
    private String name;
    @Autowired
    private Dog dog;
    @Autowired
    private Cat cat;
    public Dog getDog() {
        return dog;
    }

    public Cat getCat() {
        return cat;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

@Autowired默认是byType方式注入的,类型不唯一byName

@Resource 默认是byName方式注入的,名字不唯一byType,是java原生注解可以实现和@Autowired类似的功能

注解开发

配置文件

<?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:component-scan base-package="com.yan.annotation"/>
<!--    开启注解支持-->
    <context:annotation-config/>
</beans>

bean IOC:

模式注解:@Component,@Controller,@Service,@Repository,@Mapper,@SpringBootApplication...

属性注入:

@Autowried,@Value

全面使用注解(java config方式)

配置类

@Configuration
public class Config {
    @Bean
    public User user(){
        return new User();
    }
}

使用

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context=new AnnotationConfigApplicationContext(Config.class);
        User user = (User) context.getBean("user");
    }
}

@Configuration用于标注一个配置类,可以和@ComponentScans,@Import等注解完成诸如扫描包,合并配置文件等功能

AOP

静态代理

真实类:房东

public class Host implements Rent {
    @Override
    public void rent() {
        System.out.println("我是房东,我要出租房子");
    }
}

代理类:租房中介

public class Proxy implements Rent {
    private Rent host;
    @Override
    public void rent() {
        System.out.println("先发布租房信息");
        host.rent();
        System.out.println("出租完房子提供后续管理");
    }

    public void setHost(Rent host) {
        this.host = host;
    }
}

接口:出租

public interface Rent {
    void rent();
}

客户类:房客

public class Client{
    public static void main(String[] args) {
        Host host=new Host();
        Proxy proxy=new Proxy();
        proxy.setHost(host);
        proxy.rent();
    }
}

缺点:每个代理方法的都要手动处理代理逻辑,每个接口都要有新的代理类,冗余代码多

动态代理

动态代理的代理类是动态生成的

基于接口

jdk动态代理

Proxy 获取代理对象

InvocationHandler 调用处理程序

接口:

public interface Rent {
    void rent();
}

真实类:

public class Host implements Rent {
    @Override
    public void rent() {
        System.out.println("我是房东,我要出租房子");
    }
}

代理处理类

//这个类动态生成代理类,并用invoke方法调用原来方法,可以在其中添加代理逻辑
public class ProxyInvocationHandler implements InvocationHandler {
    private Rent rent;

    public void setHost(Rent rent) {
        this.rent = rent;
    }

    //动态生成代理类
    public Rent getProxy(){
        Rent proxyInstance = (Rent) Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
        return proxyInstance;
    }
    //处理代理实例并返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(rent, args);
        return result;
    }
}

客户类

public class Client {
    public static void main(String[] args) {
        ProxyInvocationHandler pih=new ProxyInvocationHandler();
        pih.setHost(new Host());
        Rent proxy = pih.getProxy();
        proxy.rent();
    }
}

通用代理工具类:

//通用代理类
//这个类动态生成代理类,并用invoke方法调用原来方法,可以在其中添加代理逻辑
public class CommonProxyInvocationHandler implements InvocationHandler {
    private Object realObject;

    public void setRealObject(Object realObject) {
        this.realObject = realObject;
    }

    //动态生成代理类
    public Object getProxy(){
        Object proxyInstance = Proxy.newProxyInstance(this.getClass().getClassLoader(), realObject.getClass().getInterfaces(), this);
        return proxyInstance;
    }
    //处理代理实例并返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法执行前");
        Object result = method.invoke(realObject, args);
        System.out.println("方法执行后");
        return result;
    }
}

匿名类实现

//用匿名类实现
       Object realObject= new Host();
       InvocationHandler invocationHandler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println(method.getName()+"执行前");
                Object result = method.invoke(realObject, args);
                System.out.println(method.getName()+"执行后");
                return result;
            }
        };
        Object proxyInstance = Proxy.newProxyInstance(Client.class.getClassLoader(), realObject.getClass().getInterfaces(), invocationHandler);
        ((Rent)proxyInstance).rent();

优点:一个动态代理类可以处理一类代理业务

基于类

cglib动态代理

基于 java字节码

javasist

AspectJ

使用Spring实现aop

Spring代理实际上是对JDK代理和CGLIB代理做了一层封装,并且引入了AOP概念:Aspect、advice、joinpoint等等,同时引入了AspectJ中的一些注解@pointCut,@after,@before等等.Spring Aop严格的来说都是动态代理,所以实际上Spring代理和Aspectj的关系并不大.(Aspectj有其自己的操作方法-->特殊编译方式在生成字节码阶段织入)

使用实现Spring原生Api+xml配置方式(Spring默认使用 JDK 动态代理)

增强代码

前置增强

public class BeforeExec implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.err.println(method.getName()+"执行前");
    }
}

后置增强

public class AfterExec implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.err.println(method.getName()+"执行后"+" 返回值是:"+returnValue);
    }
}

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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">
    <bean id="userService" class="com.yan.aop.service.impl.UserServiceImpl"/>
    <bean id="beforeLog" class="com.yan.aop.log.BeforeExec"/>
    <bean id="afterExec" class="com.yan.aop.log.AfterExec"/>
<!--配置aop-->
    <aop:config>
<!--配置切入点-->
        <aop:pointcut id="pointcut" expression="execution(* com.yan.aop.service.impl.*.*(..))"/>
<!-- 配置增强-->
        <aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterExec" pointcut-ref="pointcut"/>
    </aop:config>
</beans>

使用自定义切面类和切面配置切入

切面类

public class AspectLog {
    public void before(){
        System.out.println("方法执行前");
    }
    public void after(){
        System.out.println("方法执行后");
    }
}

配置文件:

<bean id="userService" class="com.yan.aop.service.impl.UserServiceImpl"/>
<bean id="aspect" class="com.yan.aop.log.AspectLog"/>
    <aop:config>
        <aop:aspect ref="aspect">
            <aop:pointcut id="point" expression="execution(* com.yan.aop.service.impl.*.*(..))"/>
            <aop:before method="before" pointcut-ref="point"/>
            <aop:after method="after" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>

这种方法无法获取真实类的一些信息

注解切入

切面类

@Aspect
public class AnnotationLog {
    //切入点的注解
    @Pointcut(value="execution(* com.yan.aop.service.impl.*.*(..))")
    private void pointcut(){}
    @Before("execution(* com.yan.aop.service.impl.*.*(..))")
    public void before(JoinPoint joinPoint){
//        System.out.println(joinPoint.getSignature());
        System.out.println("前置通知--->before");
    }
    @After("AnnotationLog.pointcut()")
    public void after(JoinPoint joinPoint){
//        System.out.println(joinPoint.getSignature());
        System.out.println("后置通知--->after");
    }
    //最终通知
    @AfterReturning(value="AnnotationLog.pointcut()", returning="result")
    public void afterReturning(Object result){
        System.out.println("后置通知");
    }
    @Around("AnnotationLog.pointcut()")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        //System.out.println("around "+joinPoint.getSignature());
        System.out.println("环绕通知前--->around after");
        Object proceed = joinPoint.proceed(joinPoint.getArgs());
        System.out.println("环绕通知后--->around after");
    }
    //异常抛出通知
    @AfterThrowing(value="AnnotationLog.pointcut()" , throwing="e")
    public void find(Throwable e ){
        System.out.println("异常抛出通知======"+e.getMessage());
    }

}

配置:

<bean id="userService" class="com.yan.aop.service.impl.UserServiceImpl"/>
<bean id="annotationAspectLog" class="com.yan.aop.log.AnnotationLog"/>
<!--    true代表开启cglib注解  -->
<aop:aspectj-autoproxy proxy-target-class="true"/>

前后顺序

环绕通知前--->前置通知-->环绕通知后--->后置通知-->最终通知

猜你喜欢

转载自www.cnblogs.com/yanshaoshuai/p/12905347.html