SpringBoot下的Spring框架学习(tedu)——day03——Spring DI、SpringAOP JDK动态代理
目录
- SpringBoot下的Spring框架学习(tedu)——day03——Spring DI、SpringAOP JDK动态代理
- 1. Spring的依赖注入
- 1.1 依赖注入案例
- 1.1.1 定义接口Pet
- 1.1.2 定义Dog类
- 1.1.3 定义User类
- 1.1.4 编辑配置类
- 1.1.5 编辑测试代码
- 1.1.6 测试结果
- 1.7 结果的流程分析(重要)
 
- 1.2 多实现类案例讲解
- 1.2.1 关于案例说明
 
- 1.3 MVC 设计思想
- 1.3.1 传统代码结构
- 1.3.2 MVC设计思想说明
- 1.3.3 "三层"代码结构
 
- 1.4 "三层"代码结构实现
- 1.4.1 代码结构说明
- 1.4.2 编辑Mapper
- 1.4.3 编辑Service
- 1.4.4 编辑Controller
- 1.4.5编辑配置类
- 1.4.6 编辑测试类
- 1.4.7 测试结果
 
- 1.5 @Value注解
- 1.5.1 编辑properties文件
- 1.5.2 编辑UserMapper
- 1.5.3 编辑UserMapperImpl
- 1.5.4 编辑测试类
- 1.5.5 关于IOC-DI总结
 
- 2. Spring-AOP
- 2.1 创建新项目
- 2.2.2 关于代理模式说明
- 2.2.3 动态代理-JDK动态代理
- 2.2.4 业务代码测试
- 2.2.5 测试结果
- 2.2.6 问题:为什么 System.out.println(proxy);会打印事务开始和事务结束?
 
 
- 常用注解
1. Spring的依赖注入
1.1 依赖注入案例
1.1.1 定义接口Pet
package com.jt.demo8;
public interface Pet {
    void hello();
}
1.1.2 定义Dog类
package com.jt.demo8;
import org.springframework.stereotype.Component;
@Component //将该类交给Spring容器管理 key:dog(当前类别首字母小写),value:反射机制创建对象
public class Dog implements Pet{
    public Dog(){
        System.out.println("Dog的无参构造");
    }
    @Override
    public void hello() {
        System.out.println("小狗汪汪汪!!!");
    }
}
1.1.3 定义User类
package com.jt.demo8;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component//将User对象交给Spring容器管理
public class User {
    public User(){
        System.out.println("我是User的无参构造");
    }
    /**
     * 注入:将spring容器中的对象进行引用
     * @Autowired: 可以将容器中对象进行注入
     *      1.按照类型注入
     *          如果注入的类型是接口,则自动的查找其实现类对象进行注入
     *          注意事项:一般spring框架内部的接口都是单实现,特殊条件下可以多实现
     *      2.按照名称注入
     */
    @Autowired
    private Pet pet;
    public void hello(){
        pet.hello();
    }
}
1.1.4 编辑配置类
package com.jt.demo8;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@ComponentScan("com.jt.demo8")
@Configuration
public class SpringConfig {
}
1.1.5 编辑测试代码
package com.jt.demo8;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class SpringDI {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
        User user = applicationContext.getBean(User.class);
        user.hello();
    }
}
1.1.6 测试结果

1.7 结果的流程分析(重要)
Spring容器启动,加载配置类SpringConfig;
 配置类SpringConfig扫描包,全局扫描,扫描到dog类;
 把dog类利用反射的机制,创建对象,会调用dog的无参构造方法;
 key指定为dog(当前类名小写),value:反射机制创建的对象
 继续扫描,扫描到User类,
 扫描到@Autowired,找到Pet接口的实现类dog,并注入给User的pet属性
 把User交给Spring容器管理
1.2 多实现类案例讲解
1.2.1 关于案例说明
一般条件下 Spring中的接口,都是单实现,如果遇到多实现,则如图所示
 
 由于没有做其它操作,所以程序必然报错.
 
 异常解决:
 User.java
 
1.3 MVC 设计思想
1.3.1 传统代码结构
说明: 如果将所有的业务代码都写到一个方法中,则导致后期维护耦合性高,为了提高程序的扩展性.将程序按照MVC设计思想 进行管理.
1.3.2 MVC设计思想说明
M: Model 数据层
 V: View 视图层
 C: Control 控制层
 总结: MVC 主要的目的降低代码的耦合性,提高扩展性.方便后续开发.
 
1.3.3 "三层"代码结构
说明: 基于MVC设计思想的启发,在后端为了提高代码的扩展性,一般将后端代码分为三层.
 分层:
- Controller层 主要与页面进行交互 @Controller
- Service层 主要实现后端的业务逻辑 @Service
- Dao层/Mapper层 主要与数据库进行交互 也把该层称之为 “持久层” @Repository/@Mapper
1.4 "三层"代码结构实现
1.4.1 代码结构说明
包名: mapper 类2个 一个接口UserMapper/一个实现类 UserMapperImpl
包名: service 类2个 一个接口UserService/ 一个实现类UserServiceImpl
包名: controller 一个类: UserController
知识说明: 被调用的一般会有接口和实现类
1.4.2 编辑Mapper
1.编辑UserMapper
package com.jt.demo9.mapper;
public interface UserMapper {
    void addUser();
}
2.编辑Mapper 实现类
package com.jt.demo9.mapper;
import org.apache.catalina.User;
import org.springframework.stereotype.Repository;
@Repository//表示持久层 该类交给Spring容器管理 key:userMapperImpl value:对象
public class UserMapperImpl implements UserMapper {
    @Override
    public void addUser() {
        System.out.println("新增用户成功!!!!");
    }
}
1.4.3 编辑Service
- 编辑UserService
package com.jt.demo9.service;
public interface UserService {
    void addUser();
}
2.编辑UserServiceImpl
package com.jt.demo9.service;
import com.jt.demo9.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService{
    @Autowired
    private UserMapper userMapper;//IOC+DI 解耦!!!!
    @Override
    public void addUser() {
        userMapper.addUser();
    }
}
1.4.4 编辑Controller
package com.jt.demo9.controller;
import com.jt.demo9.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller//key=userController
public class UserController {
    @Autowired
    private UserService userService;
    public void addUser(){
        userService.addUser();
    }
}
1.4.5编辑配置类
package com.jt.demo9.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@ComponentScan("com.jt.demo9")
@Configuration
public class SpringConfig {
}
1.4.6 编辑测试类
package com.jt.demo9;
import com.jt.demo9.config.SpringConfig;
import com.jt.demo9.controller.UserController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Spring_MVC {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
        UserController userController = applicationContext.getBean(UserController.class);
        userController.addUser();
    }
}
1.4.7 测试结果

1.5 @Value注解
1.5.1 编辑properties文件
# 1.数据结构 key=value
# 2.无需添加多余的引号
# 3.注意多余的空格
# 4.程序读取properties文件时,默认采用ISO-8859-1编码!中文必定乱码
name=张三
1.5.2 编辑UserMapper
package com.jt.demo10.mapper;
public interface UserMapper {
    void addUser();
}
1.5.3 编辑UserMapperImpl
package com.jt.demo10.mapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Repository;
//Spring根据指定的路径,加载properties配置文件 数据添加到Spring容器中
@PropertySource(value = "classpath:/addUser.properties",encoding = "UTF-8")
@Repository
public class UserMapperImpl implements UserMapper{
    /**
     * @Value 注解的作用:为属性赋值
     *        需求:从Spring容器中动态获取数据
     */
    //@Value("张三")//直接写法,扩展性不好
    @Value("${name}")
    private String name;
    @Override
    public void addUser() {
        System.out.println("新增用户:"+name);
    }
}
1.5.4 编辑测试类
package com.jt.demo10;
import com.jt.demo10.config.SpringConfig;
import com.jt.demo10.mapper.UserMapper;
import com.jt.demo10.mapper.UserMapperImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class SpringValue {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
        //获取的对象 实现类和接口都可以
//        UserMapper userMapper = applicationContext.getBean(UserMapperImpl.class);
        UserMapper userMapper = applicationContext.getBean(UserMapper.class);
        userMapper.addUser();
    }
}
1.5.5 关于IOC-DI总结
- 什么是IOC: 控制反转, 将对象交给Spring容器管理,由容器管理对象的生命周期.
- 什么是DI: 依赖注入 为当前对象注入属性(属性也是对象).
- 扩展问题: 如果采用配置文件xml的方式进行注入,则注入的方式有多种.
 ①set方式注入
 ②构造方法注入
 ③工厂模式注入
总结: 使用IOC-DI可以极大程度上实现代码的松耦合(解耦)
2. Spring-AOP
2.1 创建新项目

 2. 勾选jar包
 
 2.2 AOP代码铺垫
 2.2.1 业务说明
 事务特性: 1. 原子性 2. 一致性 3.隔离性 4.持久性
 业务说明: 在增/删除/修改的操作过程中添加事务控制.
Demo效果演示
 配置类
package com.jt.demo1.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@ComponentScan("com.jt.demo1")
@Configuration
public class SpringConfig {
}
Service层
 service层接口
package com.jt.demo1.service;
public interface UserService {
    void addUser();
    void deleteUser();
}
service层实现类
package com.jt.demo1.service;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
    @Override
    public void addUser() {
        System.out.println("完成用户新增");
    }
    @Override
    public void deleteUser() {
        System.out.println("完成用户删除操作");
    }
}
测试类
package com.jt.demo1;
import com.jt.demo1.config.SpringConfig;
import com.jt.demo1.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class SpringTx {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
        UserService userService = applicationContext.getBean(UserService.class);
        userService.addUser();
        userService.deleteUser();
    }
}
demo2
 service层实现类
package com.jt.demo1.service;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
    @Override
    public void addUser() {
        try {
            System.out.println("事务开始");
            System.out.println("完成用户新增");
            System.out.println("事务提交");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("事务回滚");
        }
    }
    @Override
    public void deleteUser() {
        try {
            System.out.println("事务开始");
            System.out.println("完成用户删除操作");
            System.out.println("事务提交");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("事务回滚");
        }
    }
}
结论:
- 如果按照上述的代码进行编辑,则所有增/删除/修改操作的代码都必须按照上述的规则.那么代码冗余.
- UserService与事务控制代码紧紧的耦合在一起.不方便后期扩展. 以后尽可能保证业务的纯粹性.
2.2.2 关于代理模式说明
说明: 在业务层不方便做,但是又不得不做的事情,可以放到代理对象中. 通过这样的设计就可以解决业务层耦合的问题. 代理对象看起来和真实的对象 一模一样.所以用户使用不会察觉.
 
 类比:
- 外卖也是一种典型的代理思想
- 游戏代练
- 房屋中介
2.2.3 动态代理-JDK动态代理
demo3
在demo1上面的基础之上,创建一个proxy包,里面创建一个JDKProxy类
package com.jt.demo1.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JDKProxy {
    /**
     * 获取代理对象
     * 参数说明:
     * 1.ClassLoader loader     类加载器 读取真实的类数据
     * 2.Class<?>[] interfaces  要求传递接口的信息
     * 3.InvocationHandler h    当代理对象执行方法时执行
     * 注意事项:JDK代理必须要求 “被代理者” 有接口要么实现接口
     *
     * @param target
     * @return
     */
    public static Object getProcy(Object target){
        //1.获取类加载器
        ClassLoader classLoader = target.getClass().getClassLoader();
        //2.获取接口
        Class<?>[] interfaces = target.getClass().getInterfaces();
        //通过代理的类新创建一个代理的对象
        return Proxy.newProxyInstance(classLoader, interfaces, getInvocationHandler(target));
    }
    //代理对象执行方法时调用
    public static InvocationHandler getInvocationHandler(Object target){
        //这些代码都是写死的
        return new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("================事务开始==============================");
                //执行目标(真实对象的)方法
                Object result = method.invoke(target, args);
                System.out.println("=========================事务提交=======================");
                return result;
            }
        };
    }
}
2.2.4 业务代码测试
package com.jt.demo1;
import com.jt.demo1.config.SpringConfig;
import com.jt.demo1.proxy.JDKProxy;
import com.jt.demo1.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class SpringTx {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
        UserService userService = applicationContext.getBean(UserService.class);
        System.out.println(userService.getClass());
        UserService proxy = (UserService)JDKProxy.getProcy(userService);
        System.out.println(proxy.getClass());
        System.out.println(proxy);
        System.out.println(proxy.toString());
        //基于代理对象,执行业务操作 实现方法扩展
        proxy.addUser();
        proxy.deleteUser();
    }
}
2.2.5 测试结果

2.2.6 问题:为什么 System.out.println(proxy);会打印事务开始和事务结束?
因为它默认打印的是proxy.toString方法,即调用了被代理类的toString方法,所以会打印事务开始和事务结束,以及其地址信息。
常用注解
@Configuration 标识当前类是配置类
@ComponentScan 包扫描注解 扫描注解
@Bean 标识该方法的返回值交给Spring容器管理
@Scope 控制多例和单例
@Lazy 懒加载
@PostConstruct 初始化方法
@PreDestroy 销毁方法
@Component 将当前类未来的对象交给容器管理
@Autowired 按照类型进行注入
@Qualifier 按照名称进行注入
@Repository 标识持久层注解
@Service 标识Service层
@Controller 标识Controller层
@Value 为属性赋值 @Value("${key}")
@PropertySource 加载指定路径的配置文件properties












![[2.2.1]进程管理——调度的概念、层次](https://img-blog.csdnimg.cn/img_convert/4badbeccc4a8bbc246d3baee80c81d5f.png)






