Spring2~~~

目录

注解配置Bean

  配置自动扫描的包

选择性实例化Bean

只扫描包下的哪些类

排除包下某种类型的注解(注解方式)

指定自动扫描哪些注解类(注解方式)

自动装配

@Value

@AutoWired

@Qualifier与@AutoWired

@Resource

泛型依赖装配

全注解开发

动态代理 

静态代理

不使用匿名内部类

 使用匿名内部类

AOP

术语

切点表达式

复用

细节

两种动态代理

jdk的Proxy

Spring的CGlib

快速入门

接口

通知类型

切面优先级

JoinPoint常用方法

后置通知中获取结果

 异常通知中获取异常

环绕通知 

全注解开发

基于XML配置AOP 


注解配置Bean

Spring的 IOC 容器检查到注解就会生成对象,但这个注解的具体含义不会识别 

其他三个注解都是@Component注解的别名 

  配置自动扫描的包

1. component-scan 要对指定包下的类进行扫描, 并创建对象到容器
2. base-package 指定要扫描的包,当spring容器创建/初始化时,就会扫描com.hspedu.spring.component包下的所有注解,将其实例化,放入到ioc容器

bean默认id是类名首字母小写 

如果要扫描spring下的包,写成com.hspedu.spring.* 
com.hspedu.spring.component会扫描comonent及其子包,牺牲一部分效率

多个包可以使用 逗号 隔开

<context:component-scan base-package="com.hspedu.spring.component"/>
如果bean目录有Order类有注解,没有指定value,就相当于
<bean id="order" class="com.spring.bean.Order"/>

选择性实例化Bean

只扫描包下的哪些类

在xml中加上 resource-pattern="User*.class" 表示只扫描spring.component 和它的子包下的User打头的类,用的比较少

<context:component-scan base-package="com.hspedu.spring.component" 
resource-pattern="User*.class" />

排除包下某种类型的注解(注解方式)

需求:如果我们希望排除某个包/子包下的某种类型的注解,可以通过exclude-filter来指定

        1.use-default-filters="true",默认是true全部生效
        2. context:exclude-filter 指定要排除哪些类,选择让谁失效
        3. type 指定排除方式 annotation表示按照注解来排除
        4. expression="org.springframework.stereotype.Service" 指定要排除的注解的全路径

    <context:component-scan base-package="com.hspedu.spring.component">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
    </context:component-scan>

指定自动扫描哪些注解类(注解方式)

需求:如果我们希望按照自己的规则,来扫描包/子包下的某些注解, 可以通过 include-filter
        1. use-default-filters="false" 表示不使用默认的过滤机制/扫描机制,所有注解全部失效
        2. context:include-filter 表示要去扫描哪些类,选择让谁生效
        3. type="annotation" 按照注解方式来扫描/过滤
        4. expression="org.springframework.stereotype.Service" 指定要扫描的注解的全路径

       <context:component-scan base-package="com.hspedu.spring.component" use-default-filters="false">
       <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
       <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
       <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
    </context:component-scan>

自动装配

@Value

用于简单类型,用于属性上,set方法上,构造方法的形参前

如果把@Value注解加到形参前面,必须是构造方法中的形参,其他方法不行

属性都写了@Value,可以不提供set方法

@Service
public class Student {
    @Value(value = "tom")
    private String name;

    private int age;

    @Value("18")
    public void setAge(int age) {
        this.age = age;
    }
}

@AutoWired

用于非简单类型,单独使用默认根据类型装配(byType)

作用在属性、set方法、构造方法、构造方法的形参上

规则

IOC容器中有多个对象,要进行查找
1、是否有唯一bean
2、根据id查找

根据id查找没找到,报错原因:按id找不到,按类型有多个,类型匹配不上

根据唯一性查找,属性名无所谓,直接按照类型找到
IOC容器中的对象和装配的对象哈希值不一定一样

多个类实现了同一个接口,类型是一样的 

 

    <context:component-scan
            base-package="com.hspedu.spring.component"/>
    <!--配置两个UserService对象-->
    <bean class="com.hspedu.spring.component.UserService" id="userService200"/>
    <bean class="com.hspedu.spring.component.UserService" id="userService300"/>
@Controller
public class UserAction {
    @Autowired
    private UserService userService200;//如果不是唯一bean,会根据id查找
}

@Qualifier与@AutoWired

必须搭配使用,相当于@Resource
如果有多个bean,则根据id进行装配

@Service("orderService")
public class OrderService {
    @Autowired
    @Qualifier("id")
    private OrderDao orderDao;
}

@Resource

作用在属性、set方法上

 按照name的值在容器中查找,属性名没有作用了

按照type类型查找,属性名没有作用了,要求在容器里只能有一个这样类型的对象 

  

如果没有指定name和type,则先试用byName注入策略,如果匹配不上,再使用byType策略

泛型依赖装配

        使用Autowired
        把PhoneDao对象装配到PhoneService,
通过泛型传入BaseDao,再把指定的泛型对象装配到PhoneService

全注解开发

@Configuration
@ComponentScan({"包1","包2"})
public class Spring6Config {

}
public class T {
    @Test
    public void m1() {
        AnnotationConfigApplicationContext ioc = 
                new AnnotationConfigApplicationContext(Spring6Config.class);
        ioc.getBean();
    }
}

动态代理 

不改变原有代码的情况下进行对象功能增强,使用代理对象拓展功能
将目标对象作为代理对象的一个属性

静态代理

一个接口就要写一个代理类,会类爆炸,进行优化 => 不写代理类,动态生成class字节码,这个字节码就是代理类 

不使用匿名内部类

需要实现InvocationHandler接口

代理对象 

Proxy.newProxyInstance这个方法做了两件事

1、在内存中动态生成了一个代理类的字节码class

2、new对象了。通过内存中生成的代理类这个代码,实例化了代理对象

三个参数

1、类加载器,目标类和代理类的加载器使用同一个

2、告诉代理类,目标类要实现哪一个或哪一些接口

3、InvocationHandler(调用处理器,是一个接口),写接口实现类或内部类

当代理对象调用代理方法的时候,invoke()方法会被调用

 invoke(Object proxy, Method method, Object[] args) 

三个参数

1、代理对象的引用。使用较少

2、目标对象上的目标方法,通过代理对象调用方法时的那个方法

3、目标方法上的实参

invoke要将目标对象的执行结果继续返回

 

public class Handler implements InvocationHandler {
    private Object target;

    public Handler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("增强1");
        Object retValue = method.invoke(target, args);
        System.out.println("增强2");
        return null;
    }
}
public class Client {
    public static void main(String[] args) {
        //创建目标对象
        OrderService target = new OrderServiceImpl();
        //创建代理对象,向下转型
        OrderService proxyObj = (OrderService)Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),new Handler(target));
        //调用代理对象的代理方法
        proxyObj.run();
    }
}

封装 

public class ProxyUtil {
    public static Object newProxyInstance(Object target) {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),new Handler(target));
    }
}

 使用匿名内部类

公共接口

public interface Vehicle {
    public void run();
    public String fly(int height);
}

 目标对象

public class Car implements Vehicle{
    @Override
    public void run() {
        System.out.println("小汽车在路上 running....");
    }

    @Override
    public String fly(int height) {
        System.out.println("小汽车可以飞翔 高度=" + height);
        return "小汽车可以飞翔 高度=" + height;
    }
}

 代理对象

public class VehicleProxyProvider {
    
    //target_vehicle 表示真正要执行的对象
    //该对象的类实现了Vehicle接口
    private Vehicle target_vehicle;
    
    public VehicleProxyProvider(Vehicle target_vehicle) {
        this.target_vehicle = target_vehicle;
    }

    //返回代理对象
    public Vehicle getProxy() {
        
        ClassLoader classLoader =
                target_vehicle.getClass().getClassLoader();

        Class<?>[] interfaces = target_vehicle.getClass().getInterfaces();


        //通过匿名对象的方式来创建该对象
        InvocationHandler invocationHandler = new InvocationHandler() {
            @Override
            public Object invoke(Object o, Method method, Object[] args)
                    throws Throwable {

                System.out.println("交通工具开始运行了....");
                //method 是?: public abstract void com.hspedu.spring.proxy2.Vehicle.run()
                //通过反射+动态绑定机制,执行被代理对象的方法
                Object result = method.invoke(target_vehicle, args);
                System.out.println("交通工具停止运行了....");
                return result;
            }
        };

        Vehicle proxy =
                (Vehicle)Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
        return proxy;
    }
}

Test类 

    @Test
    public void proxyRun() {
        //创建VehicleProxyProvider对象, 并且我们传入的要代理的对象
        VehicleProxyProvider vehicleProxyProvider =
                new VehicleProxyProvider(new Car());
        //获取代理对象, 该对象可以代理执行方法
        //proxy的编译类型是 Vehicle, 运行类型是 class com.sun.proxy.$Proxy9
        Vehicle proxy = vehicleProxyProvider.getProxy();
        //当执行run方法时,会执行到 代理对象的invoke
        proxy.run();
        String result = proxy.fly(10000);
        System.out.println("result=" + result);
    }

AOP

面向切面编程

术语

与事务不挂钩的叫做交叉业务

切点表达式

切点表达式用来定义通知(Advice)往哪些方法上切入
切点表达式可以指向类的方法,也可以指向接口的方法,也可以对没有实现接口的类进行切入

execution([访问控制权限修饰符] 返回值类型 [全限定类名]方法名(形式参数列表) [异常])

访问控制权限修饰符:

  • 可选项。
  • 没写,就是4个权限都包括。
  • 写public就表示只包括公开的方法。

返回值类型:

  • 必填项。
  • * 表示返回值类型任意。

全限定类名:

  • 可选项。
  • 两个点“..”代表当前包以及子包下的所有类。
  • 省略时表示所有的类。

方法名:

  • 必填项。
  • *表示所有方法。
  • set*表示以set开始的所有方法 。

形式参数列表:

  • 必填项
  • () 表示没有参数的方法
  • (..) 参数类型和个数随意的方法
  • (*) 只有一个参数的方法
  • (*, String) 第一个参数类型随意,第二个参数是String的。

异常:

  • 可选项。
  • 省略时表示任意异常类型。

复用

    //定义一个切入点, 在后面使用时可以直接引用, 提高了复用性
    @Pointcut(value = "execution(public float spring.aspectj.SmartDog.getSum(float, float)))")
    public void myPointCut() {}

    @Before(value = "myPointCut()")
    public void showBeginLog(JoinPoint joinPoint) {}

    @AfterReturning(value = "myPointCut()", returning = "res")
    public void showSuccessEndLog(JoinPoint joinPoint, Object res) {}

    @AfterThrowing(value = "myPointCut()", throwing = "throwable")
    public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {}

 细节

按接口来获取,多个类实现了接口会报错
UsbInterface interface = ioc.getBean(UsbInterface.class);

interface是代理对象,获取Phone类的代理对象,getBean()就相当于之前的getPorxy()
UsbInterface interface = (UsbInterface)ioc.getBean(Phone.class);
UsbInterface interface = (UsbInterface)ioc.getBean("phone");

两种动态代理

jdk的Proxy

面向接口,只能增强实现类中接口中存在的方法
得到的对象是JDK代理对象的实例

生成的代理对象只能转换成接口不能转换成被代理类

 

Spring的CGlib

面向父类,可以增强父类的所有方法
得到的对象是被代理对象的子类

快速入门

@Component
public class OrderService {
    // 目标方法
    public void generate(){
        System.out.println("订单已生成!");
    }
}
@Aspect
@Component
public class MyAspect {
    @Before("execution(* com.powernode.spring6.service.OrderService.*(..))")
    // 这就是需要增强的代码(通知)
    public void advice(){
        System.out.println("我是一个通知");
    }
}
@Test
    public void testAOP(){
        ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-aspectj-aop-annotation.xml");
        OrderService orderService = ioc.getBean("orderService", OrderService.class);
        orderService.generate();
    }

接口

public interface SmartAnimalable {
    //求和
    float getSum(float i, float j);
    //求差
    float getSub(float i, float j);
}

实现类 

@Component //使用@Component 当spring容器启动时,将 SmartDog注入到容器
public class SmartDog implements SmartAnimalable {
    @Override
    public float getSum(float i, float j) {
        float result = i + j;
        System.out.println("方法内部打印result = " + result);
        return result;
    }

    @Override
    public float getSub(float i, float j) {
        float result = i - j;
        System.out.println("方法内部打印result = " + result);
        return result;
    }
}

切面类 

  • showBeginLog方法可以理解成就是一个切入方法
  • JoinPoint joinPoint 在底层执行时,由AspectJ切面框架, 会给该切入方法传入 joinPoint对象,通过该方法,程序员可以获取到 相关信息
@Aspect
@Component
public class SmartAnimalAspect {
    @Before(value = "execution(public float spring.aspectj.SmartDog.getSum(float, float))")
    public void showBeginLog(JoinPoint joinPoint) {
        //通过连接点对象joinPoint 可以获取方法签名
        //invoke(Object proxy, Method method, Object[] args)整合到joinPoint
        Signature signature = joinPoint.getSignature();
        System.out.println(signature.getName() + "-参数 "
                + Arrays.asList(joinPoint.getArgs()));
    }
}

XML开启AOP功能

开启基于注解的AOP功能,spring容器在扫描类的时候,查看该类上是否有@Aspect注解,如果有,则给这个类生成代理对象

如果不开启,.getClass()不会得到代理对象,只会得到当前运行的对象(相当于new)


proxy-target-class,ture表示强制使用CGLIB动态代理,默认false,使用JDK动态代理

    <context:component-scan
            base-package="com.hspedu.spring.aop.aspectj"/>

    <aop:aspectj-autoproxy proxy-target-class="false"/>

Test类

    @Test
    public void smartDogTestByProxy() {
        ApplicationContext ioc =
                new ClassPathXmlApplicationContext("beans08.xml");
        //需要通过接口类型来获取到注入的SmartDog对象-就是代理对象
        SmartAnimalable smartAnimalable =
                ioc.getBean(SmartAnimalable.class);
        //SmartAnimalable smartAnimalable =
        //        (SmartAnimalable)ioc.getBean("smartDog");
        smartAnimalable.getSum(10, 2);
    }

以前是new一个对象,获得这个类的的代理对象

动态代理返回的对象是个代理对象,强转成接口的形式返回

Animal proxy =

        (Animal)Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);

return proxy;
(xml中开启AOP)现在通过接口类型获取的对象就是这个类的代理对象

通知类型

  • 前置通知:@Before 目标方法执行之前的通知
  • 后置通知:@AfterReturning 目标方法执行之后的通知
  • 环绕通知:@Around 目标方法之前添加通知,同时目标方法执行之后添加通知。
  • 异常通知:@AfterThrowing 发生异常之后执行的通知
  • 最终通知:@After 放在finally语句块中的通知

环绕是最大的通知,发生异常不会有后置通知和后环绕 

切面优先级

如果同一个方法,有多个切面在同一个切入点切入,优先级如何控制

注解@Order(value = n) 来控制,n 越小,优先级越高

前置通知通过@Order来判断顺序

JoinPoint常用方法

joinPoint.getSignature()获取方法签名

后置通知中获取结果

在@AfterReturning 增加属性 , 比如 returning = "res",同时在切入方法增加 Object res
注意: returning = "res" 和 Object res 的 res名字一致

 异常通知中获取异常

环绕通知 

@Aspect
@Component
public class SmartAnimalAspect {
    //演示环绕通知的使用-了解
    //1. @Around: 表示这是一个环绕通知[完成其它四个通知的功能]
    //2. value = "execution(public float com.hspedu.spring.aop.aspectj.SmartDog.getSum(float, float)) 切入点表达式
    //3. doAround 表示要切入的方法 - 调用结构 try-catch-finally
    @Around(value = "execution(public float com.hspedu.spring.aop.aspectj.SmartDog.getSum(float, float))")
    public Object doAround(ProceedingJoinPoint joinPoint) {
        Object result = null;
        String methodName = joinPoint.getSignature().getName();
        try {
            //1.相当于前置通知完成的事情
            Object[] args = joinPoint.getArgs();
            List<Object> argList = Arrays.asList(args);
            System.out.println("AOP环绕通知[-前置通知]" + methodName + "方法开始了--参数有:" + argList);
            //在环绕通知中一定要调用joinPoint.proceed()来执行目标方法
            result = joinPoint.proceed();
            //2.相当于返回通知完成的事情
            System.out.println("AOP环绕通知[-返回通知]" + methodName + "方法结束了--结果是:" + result);
        } catch (Throwable throwable) {
            //3.相当于异常通知完成的事情
            System.out.println("AOP环绕通知[-异常通知]" + methodName + "方法抛异常了--异常对象:" + throwable);
        } finally {
            //4.相当于最终通知完成的事情
            System.out.println("AOP环绕通知[-后置通知]" + methodName + "方法最终结束了...");
        }
        return result;
    }
}

全注解开发

@Configuration//代替配置文件
@ComponentScan("com.powernode.spring6.service")//组件扫描
@EnableAspectJAutoProxy(proxyTargetClass = true)//开启AOP
public class Spring6Configuration {
}
@Test
public void testAOPWithAllAnnotation(){
    ApplicationContext ioc = new AnnotationConfigApplicationContext(Spring6Configuration.class);
    OrderService orderService = ioc.getBean("orderService", OrderService.class);
    orderService.generate();
}

基于XML配置AOP 

public interface SmartAnimalable {
    float getSum(float i, float j);
    float getSub(float i, float j);
}
public class SmartDog implements SmartAnimalable {
    @Override
    public float getSum(float i, float j) {return result;}
    @Override
    public float getSub(float i, float j) {return result;}
}

切面类将注解全部去掉 

public class SmartAnimalAspect {
    public void showBeginLog(JoinPoint joinPoint) {}
    public void showSuccessEndLog(JoinPoint joinPoint, Object res) {}
    public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {}
    public void showFinallyEndLog(JoinPoint joinPoint) {}
}

测试类 

    @Test
    public void testAspectByXML() {
        ApplicationContext ioc = new ClassPathXmlApplicationContext("beans09.xml");
        SmartAnimalable smartAnimalable =
                ioc.getBean(SmartAnimalable.class);
        smartAnimalable.getSum(10, 2);
    }

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 http://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">

    <!--使用XML配置,完成AOP编程-->
    <!--配置一个切面类对象-bean-->
    <bean class="com.hspedu.spring.aop.xml.SmartAnimalAspect" id="smartAnimalAspect"/>
    <!--配置一个SmartDog对象-bean-->
    <bean class="com.hspedu.spring.aop.xml.SmartDog" id="smartDog"/>
    <!--配置切面类, 细节一定要引入 xmlns:aop-->
    <aop:config>
        <!--配置切入点-->
        <aop:pointcut id="myPointCut" expression="execution(public float com.hspedu.spring.aop.xml.SmartDog.getSum(float, float)))"/>
        <!--配置切面的前置,返回, 异常, 最终通知-->
        <aop:aspect ref="smartAnimalAspect" order="10">
            <!--配置前置通知-->
            <aop:before method="showBeginLog" pointcut-ref="myPointCut"/>
            <!--返回通知-->
            <aop:after-returning method="showSuccessEndLog" pointcut-ref="myPointCut" returning="res"/>
            <!--异常通知-->
            <aop:after-throwing method="showExceptionLog" pointcut-ref="myPointCut" throwing="throwable"/>
            <!--最终通知-->
            <aop:after method="showFinallyEndLog" pointcut-ref="myPointCut"/>
            <!--配置环绕通知-->
            <!--<aop:around method=""/>-->
        </aop:aspect>
    </aop:config>
</beans>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值