千锋2023课件61课spring

今日内容

0 复习昨日
1 Bean细节
2 注解实现【重点】
3 代理模式 【理解原理】
4 AOP 【重点】

0 复习昨日

1 spring介绍

  • spring是一个轻量级J2EE开发框架,用来管理bean的一个容器.由单元测试,核心容器,AOP,DAO,ORM,JDBC,TX,Web,MVC等部分组成,
  • 我们主要学习使用两个核心内容: IOC,AOP

2 ioc是什么

  • IOC控制反转,是指将创建对象的控制权反转给spring容器
  • spring容器其实就是配置文件applicationContext.xml
  • 一个标签就会创建一个对象<bean id="" class="">

3 di是什么

  • DI依赖注入,属性赋值
  • set方法注入
    • 要赋值的属性,得有对应的set方法
    • 具体是在bean标签内部使用<property name="属性名" value="基本类型值" ref="引用类型,引用另一个对象的id">
  • 构造方法注入
  • 自动注入

4 自动注入是怎么注入的

  • 需要先在bean标签中设置自动注入的属性autowire
  • 指定自动注入的方式autowire=“byType|byName”
  • byType
    • 例如: UserServiceImpl中需要注入UserDao类型的属性
    • spring容器中有该UserDao类型的bean,就会注入
    • 但是!!!如果容器中有不止一个该UserDao类型的bean,就会注入失败!!
  • byName
    • 此时就可以使用byName来指定,注入哪个UserDao类型的bean
    • 容器中UserDao的id与UserServiceImpl类中UserDao属性名一致

1 Bean的细节

IOC是可以控制创建对象时是: 单列还是多例

单例: 单实例,即单对象,也就是说这个类有且仅有一个对象

多例: 多实例,即多个对象,也就是说这个类可以有多个对象

<bean id="..." class="..." scope="singleton|prototype">
    
<!--
IOC,默认创建对象是单实例(scope="singleton"),即每次从容器中获得的是同一个对象
设置成scope="prototype",那就多实例,即每次从容器获得都是新对象
-->
 <bean id="myClass" class="com.qf.model.MyClass" scope="prototype">
   <property name="age" value="18"/>
   <property name="name" value="京超啊"/>
</bean>
    @Test
    public void testIOC(){
        String path = "applicationContext.xml";
        // 通过配置文件,创建出容器
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(path);

        // 从容器获得对象
        MyClass myClass1 = context.getBean("myClass", MyClass.class);
        System.out.println(myClass1.hashCode());
		// 如果是单例,每次都是同一个地址
        MyClass myClass2 = context.getBean("myClass", MyClass.class);
        System.out.println(myClass2.hashCode());
		// 如果是多例,每次地址都不一样
        MyClass myClass3 = context.getBean("myClass", MyClass.class);
        System.out.println(myClass3.hashCode());
    }

2 注解开发IOC【重点】

以前就可以还使用@WebServlet和@WebFilter注解,省去web.xml中写的配置

以后工作实际开发中,都会使用注解开发,效率很高!

IOC今天也要变成注解开发,用来省去在applicationContext.xml中的配置

IOC相关注解

注解解释
@Controller创建对象,用在控制层
@Service创建对象,用在业务层
@Repository创建对象,用在数据访问层
@Component创建对象,其他地方用
@Scope控制对象的单例|多例

以上注解可以取代配置文件中的<bean>标签

DI相关注解

注解解释
@Value给基本类型注入
@Autowired给引用类型自动注入(默认按照类型注入)byType
@Resource给引用类型自动注入(默认按照名字注入)byName
@Qualifier和@Autowired联用,可以实现按名字注入

以上注解可以取代配置文件中的<property>标签

2.1 UserService和UserDao

UserService&UserServiceImpl

public interface UserService {
    void findUserById();
}
@Service // 相对于是<bean id="" class="">标签,默认id是当前类名首字母小写
public class UserServiceImpl implements UserService {

    @Autowired  // 相对于是autowire=byType属性
    private UserDao userDao;

    public void findUserById() {
        System.out.println("UserServiceImpl--->业务层执行" );
        userDao.findUserById();
    }
}

UserDao&UserDaoImpl

public interface UserDao {
    void findUserById();
}

@Repository // 相对于写bean标签,spring就会创建对象
public class UserDaoImpl implements UserDao {
    public void findUserById() {
        System.out.println("UserDaoImpl --> 查询数据库");
    }
}

2.2 配置文件开启注解扫描

<?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
       http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 扫描注解,得让容器知道哪些类需要由Spring创建对象 -->
    <context:component-scan base-package="com.qf"/>

</beans>

2.3 测试

    @Test
    public void testIOCandDIByAnno(){
        String path = "applicationContextByAnno.xml";
        // 通过配置文件,创建出容器
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(path);

        // 使用注解,默认id即对象名,是类名首字母小写
        UserService userService = context.getBean("userServiceImpl", UserService.class);
        userService.findUserById();
    }

2.4 注解使用细节

  1. IOC创建对象的注解,四个功能一样,都可以创建对象,只不过建议不同的位置使用不同的注解,见名知意

  2. 加上创建对象的注解后,默认对象名是类名首字母小写,即需要通过类名首字母小写从容器中获得对象

  3. 其实可以在注解中设置参数,给对象取名.例如@Service(“aaa”),以后就用aaa从容器中取值

  4. 自动注入时@Autowired ,默认按照类型注入

  5. 但是如果容器中有两个该类型的bean,自动注入就会失败,此时可以按名字注入

    @Resource(name="userDaoImpl2")
    
  6. 如果不使用@Resource按名字注入,也可以使用@Qualifier("userDaoImpl2")配合@Autowired一起实现,按照名字注入

  7. 可以给类加@Scope(“prototype|singleton”) 来控制单例多例

2.5 基本类型赋值【了解】

package com.qf.model;

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

import java.util.Date;

/**
 * --- 天道酬勤 ---
 *
 * @author QiuShiju
 * @desc
 */
@Component
public class User {

    @Value("20")
    private int age;
    @Value("靖凯")
    private String name;
    @Value("2000/01/01")
    private Date birthday;

    // set get
}

3 代理设计模式

代理的设计理念是限制对象的直接访问,即不能通过 new 的方式得到想要的对象,而是访问该对象的代理类。

这样的话,我们就保护了内部对象,如果有一天内部对象因为 某个原因换了个名或者换了个方法字段等等,那对访问者来说 一点不影响,因为他拿到的只是代理类而已,从而使该访问对 象具有高扩展性。

代理类可以实现拦截方法,修改原方法的参数和返回值,满足 了代理自身需求和目的,也就是代理的方法增强性。

按照代理的创建时期,代理可分为:静态代理动态代理

静态代理由开发者手动创建,在程序运行前,已经存在;

动态代理不需要手动创建,它是在程序运行时动态的创建代理类。


总结:代理模式–给某个目标对象提供一个代理,以改变对该对象的访问方式,以便于对目标方法的增强

3.1 静态代理

静态代理,在运行之前,提前先把代理创建好.

需求: 目标类(Fangdong),目标方法(chuzu()),来一个代理FangdongProxy(中介),中介会在目标方法执行前后,实现一些增强的功能.

演示:

目标接口和实现类

/**
 * --- 天道酬勤 ---
 *
 * @author QiuShiju
 * @desc 房东接口
 */
public interface Fangdong {
    void chuzu();
}

public class FangdongImpl implements Fangdong{
    public void chuzu() {
        System.out.println("房东出租房!" );
    }
}

代理

/**
 * --- 天道酬勤 ---
 *
 * @author QiuShiju
 * @desc 房东代理
 */
public class FangdongProxy {

    private Fangdong fangdong;

    public FangdongProxy(Fangdong fangdong){
        this.fangdong = fangdong;
    }

    // 代理执行方法
    public void chuzu(){

        // 前: 功能增强
        System.out.println("租房前: 中介发布消息");
        // 目标方法:出租房
        fangdong.chuzu();
        // 后: 功能增强
        System.out.println("中介签合同/后期水电气的维修" );
    }
}

测试

public static void main(String[] args) {

    // 找代理
    FangdongProxy proxy = new FangdongProxy(new FangdongImpl( ));
    proxy.chuzu();
}

租房前: 中介发布消息
房东出租房!
中介签合同/后期水电气的维修

以上就是静态代理,FangdongProxy在程序运行前,是提前创建好的.

现在需要有一人需要买车,有汽车厂商,就需要一个汽车代理商,代理商在真正执行卖车前后可以做一些增强.即需要再创建汽车厂的代理

但是.如果需要有一人需要买酒,有酒厂,就需要酒厂的代理,代理商在真正执行卖酒前,后可以做一些增强,即需要再创建酒厂的代理

…依次类推

这样下去,每做一件事情,就需要一个代理,代理类越来越多,多个代理类的增强的功能代码冗余


既然是这样,那能不能根据目标类,动态产生代理呢?

答案是可以的,那就是动态代理!

3.2 动态代理

动态代理: 在程序运行过程中,动态的为目标类产生代理类

实现动态代理有两种方案:

  • jdk动态代理(JDK自带)
    • jdk动态代理,只能代理接口,即目标类必须有接口
  • cglib动态代理(第三方技术,需要导入第三方jar包)
    • cglib动态代理,目标类可以是接口也可以是实现类

3.2.1 JDK动态代理

在java的java.lang.reflect.InvocationHandler包下有一个InvocationHandler接口,可以实现动态产生代理对象.

package com.qf.proxy.dynamic.jdk;

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

/**
 * --- 天道酬勤 ---
 *
 * @author QiuShiju
 * @desc
 */
public class MyInvocationHandler implements InvocationHandler {

    private Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }
    /**
     * @param proxy   代理对象
     * @param method  目标方法
     * @param args    目标方法执行需要的参数
     * @return 目标方法执行后返回值
     * @throws Throwable
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // 目标方法前:
        System.out.println("前期宣传" );


        // 目标方法执行
        Object ret = method.invoke(target,args);


        // 目标方法后:
        System.out.println("后期维护" );
        return ret;
    }
}

测试

public class TestJDKDynamic {

    public static void main(String[] args) {

        // 动态代理,只需要给定目标类,就会产生目标类的代理对象

        // Class clazz = FangdongImpl.class;
        // ClassLoader loader = clazz.getClassLoader( );
        // Class[] interfaces = clazz.getInterfaces( );
        // MyInvocationHandler handler = new MyInvocationHandler(new FangdongImpl( ));
        // /**
        //  * @param   loader 被代理的类(目标类)的类加载器
        //  * @param   interfaces 被代理的类(目标类)的实现的接口数组
        //  * @param   handler 刚才自己创建的MyInvocationHandler
        //  * @return 返回代理对象
        //  */
        // Fangdong proxy = (Fangdong) Proxy.newProxyInstance(loader, interfaces, handler);
        //
        // proxy.chuzu();


        Class clazz = CarFactoryImpl.class;
        ClassLoader loader = clazz.getClassLoader( );
        Class[] interfaces = clazz.getInterfaces( );
        MyInvocationHandler handler = new MyInvocationHandler(new CarFactoryImpl( ));
        /**
         * @param   loader 被代理的类(目标类)的类加载器
         * @param   interfaces 被代理的类(目标类)的实现的接口数组
         * @param   handler 刚才自己创建的MyInvocationHandler
         * @return 返回代理对象
         */
        CarFactory proxy = (CarFactory) Proxy.newProxyInstance(loader, interfaces, handler);

        proxy.sellCar();

        //  JDK的动态代理,目标类必须实现接口,否则代理失败
    }
}

3.2.2 CGLIB动态代理

CGLIB是第三方技术,需要导入第三方jar包

但是Spring框架已经整合了CGLIB技术,即在导入spring依赖时,已经导入了cglib包了

在这里插入图片描述

package com.qf.proxy.dynamic.cglib;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * --- 天道酬勤 ---
 *
 * @author QiuShiju
 * @desc
 */
public class MyProxyInterceptor implements MethodInterceptor {

    // cglib增强器
    private Enhancer enhancer = new Enhancer();

    public MyProxyInterceptor(Class targetClass){
        enhancer.setSuperclass(targetClass);
        enhancer.setCallback(this);
    }

    // 获得代理对象
    public Object getProxyBean() {
        return enhancer.create();
    }


    /**
     * @param target  目标对象(被代理对象)
     * @param method  目标方法
     * @param args  目标方法的参数
     * @param methodProxy 代理目标方法
     * @return 目标方法执行后,返回数据
     * @throws Throwable
     */
    public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

        // 前:
        System.out.println("前期宣传" );

        // 目标方法
        Object ret = methodProxy.invokeSuper(target, args);

        // 后:
        System.out.println("后期维护" );

        return ret;
    }
}
package com.qf.proxy.dynamic.cglib;

/**
 * --- 天道酬勤 ---
 *
 * @author QiuShiju
 * @desc
 */
public class TestCGLIB {

    public static void main(String[] args) {

        // 动态代理,只需要给定目标类,就会产生目标类的代理对象

        // MyProxyInterceptor myProxyInterceptor = new MyProxyInterceptor(FangdongImpl.class);
        // Fangdong proxy = (Fangdong) myProxyInterceptor.getProxyBean( );
        // proxy.chuzu();

        MyProxyInterceptor myProxyInterceptor = new MyProxyInterceptor(CarFactoryImpl.class);
        CarFactoryImpl proxy = (CarFactoryImpl) myProxyInterceptor.getProxyBean( );
        proxy.sellCar();

        // CGLIB代理的好处,就在于目标类即可以实现接口,也可以不用实现接口,都可以代理

    }
}

3.3 代理总结

需要理解: 什么是代理? 为什么需要代理? 代理的类型?

动态代理的代码,不需要去记,知道两种不同动态代理方案的特点.


理解几个词汇

  • 目标类
  • 目标方法
  • 代理类
  • 增强

作业

【重点】注解开发 - 2遍 - 熟练
【理解】代理 - 抄一遍,主要是理解
预习AOP

4 AOP

aop的底层原理就是动态代理(JDK,CGLIB)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

源码头

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值