Spring

Spring

1.1 简介

  • Spring:春天------>给软件行业带来了春天!
  • 2002,首次推出了Spring框架的雏形:interface21框架!
  • Spring框架即以interface21框架为基础,经过重新设计,并不断丰富其内涵,于2004年3月24日发布了1.0正式版。
  • Rod Johnson,Spring Framework创始人,著名作者。很难想象Rod Johnson的学历,真的让好多人大吃一惊,他是悉尼大学的博士,然而他的专业不是计算机,而是音乐学。
  • Spring理念:是为了解决企业级应用开发的复杂性而创建的,简化开发,本身是一个大杂烩,整合了现有的技术框架!
  • SSH:Struct2 + Spring + Hibernate!
  • SSM:SpringMVC + Spring + Mybatis!
官网:https://spring.io/projects/spring-framework#overview
官方下载地址:https://repo.spring.io/release/org/springframework/spring/
GitHub:https://github.com/spring-projects/spring-framework

maven

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <!--这个依赖里面东西比较多,相当于导了很多个需要的包-->
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.6</version>
</dependency>

Spring是一个免费的开源框架

Spring是一个轻量级、非入侵式的框架

控制反转(IOC)面向切面编程(AOP)

支持事务的处理,对框架整合的支持

Spring是一个轻量级的控制反转和面向切面编程的框架

弊端:由于发展了太久,容纳的技术越来越繁多,导致配置十分繁琐

Spring的核心Ioc介绍:

  1. 依赖:CalssA使用了ClassB的属性或者方法,ClassA依赖ClassB。

  2. Ioc: 控制反转,是一个概念,是一个思想。用来指导我们如何创建、管理、使用对象的。

  3. 控制:控制对象的创建,属性的赋值,依赖关系的管理。以及对象从创建到销毁的整个生命周期。

  4. 反转:把开发人员在代码中创建到对象的权限转移给代码之外的容器(Spring)实现,由容器实现创建对象,管理。

  5. 正转:源代码中由开发人员,使用new 构造方法创建对象。

Ioc简单的说就是由别人创建对象,自己在代码中使用已经创建好的对象。Spring是容器,它可以创建和管理对象,管理依赖关系

  • DI 在 Spring 上通过反射机制来实现 Ioc
  • 配置文件中所有的对象,会在容器创建时创建出来
  • Spring容器底层是一个Map,bean的id和class属性是以 K V 的方式存放在这个Map集合中的
  • 实体类对象一般不交给容器来创建。Servlet对象是属于TomCat来创建的,不能交给Spring容器来创建
  • Spring容器默认是使用类的无参构造来创建对象的

Bean有参构造赋值

实体类

public class ThingOne {
    private ThingTwo two;
    private ThingThree three;
    //没有无参构造
    public ThingOne(ThingTwo two,ThingThree three){
        this.two = two;
        this.three = three;
        System.out.println("有参构造");
    }
}

public class ThingTwo {

    public ThingTwo(){
        System.out.println("two");
    }
}

public class ThingThree {

    public ThingThree(){
        System.out.println("three");
    }
}

2

public class IndexBean {
    private int id;
    private String name;

    public IndexBean(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

public class exampleBean {

    private int id;
    private String name;

    public exampleBean(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

applicationContext.xml

<!--默认无参构造-->
<bean id="serviceImpl" class="com.zsh.service.UserServiceImpl">
    <property name="dao" ref="userImpl"/>
</bean>

<!--ThingTest使用有参构造方法 -->
<!--1、参数为引用类型-->
<bean id="one" class="com.zsh.pojo.ThingOne">
    <constructor-arg ref="two"/>
    <constructor-arg ref="three"/>
</bean>
<bean id="two" class="com.zsh.pojo.ThingTwo">
</bean>
<bean id="three" class="com.zsh.pojo.ThingThree">
</bean>
<!--2、基本类型参数,name在参数较少的情况下可以省略-->
<bean id="example" class="com.zsh.pojo.exampleBean">
    <constructor-arg type="int" name="id" value="121"/>
    <constructor-arg type="java.lang.String" name="name" value="你好"/>
</bean>
<!--3、根据下标赋值-->
<bean name="index" class="com.zsh.pojo.IndexBean">
    <constructor-arg index="0" value="1"/>
    <constructor-arg index="1" value="张三"/>
</bean>

测试

@Test
public void test1(){
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserServiceImpl serviceImpl = (UserServiceImpl)context.getBean("serviceImpl");
    serviceImpl.getUser();
}

@Test
public void ThingTest1(){
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    ThingOne one = (ThingOne) context.getBean("one");
}
@Test
public void ThingTest2(){
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    exampleBean one = (exampleBean) context.getBean("example");
    System.out.println(one);
}
@Test
public void ThingTest3(){
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    IndexBean one = (IndexBean) context.getBean("index");
    System.out.println(one);
}

别名

<!--如果添加了别名,通过别名也可以获取对象-->
<alias name="user" alias="userAlias"/>

Bean的配置

<!--
        id: bean的唯一标识符,也就是相当于我们学的对象名
        class: bean对象的全限定名:包名 + 类型
        name: 也是别名 而且name可以同时设置多个别名,可以用逗号 空格 分号隔开
    -->
<bean id="user1" class="com.yang.entity.User" name="test test1, test2; test3"> 
    <constructor-arg type="int" value="18"/>
    <constructor-arg type="java.lang.String" value="张三"/>
</bean>

import

import,一般用于团队开发使用,他可以将多个配置文件,导入合并为1个
假设,现在项目中又多个人开发,这三个人负责不同的类开发,不同的类需要注册在不同的bean中,我们可以用import将所有人的beans.xml合并为一个总的!

  • appliacationContext1.xml
  • appliacationContext2.xml
  • appliacationContext3.xml
  • 合并
	<import resource="applicationContext1.xml"/>

Spring DI(依赖注入)

构造器和set注入

环境

public class Student {
    private String name;
    private Address address;
    private String[] books;
    private List<String> hobbys;
    private Map<String,String> card;
    private Set<String> games;
    private String girlFriends;
    private Properties info;
}

public class Address {
    private String address;

    public Address() {
    }
}
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="student" class="com.zsh.pojo.Student">
        <!--基本类型注入-->
        <property name="name" value="猪鼻"/>
        <!--引用类型注入-->
        <property name="address" ref="address"/>
        <!--数组注入-->
        <property name="books">
            <array>
                <value>java修炼手册</value>
                <value>mysql修炼手册</value>
                <value>spring boot修炼手册</value>
                <value>spring cloud修炼手册</value>
            </array>
        </property>
        <!--List注入-->
        <property name="hobbys">
            <list>
                <!--如果要添加引用类型使用<ref>标签-->
                <value>吃饭</value>
                <value>吃饭</value>
                <value>吃饭</value>
            </list>
        </property>
        <!--Map注入-->
        <property name="card">
            <map>
                <entry key="招商银行" value="543123543213"/>
                <entry key="农业银行" value="643123543213"/>
                <entry key="邮储银行" value="743123543213"/>
                <entry key="工商银行" value="843123543213"/>
            </map>
        </property>
        <!--Set-->
        <property name="games">
            <set>
                <value>LOL</value>
                <value>aa</value>
                <value>bb</value>
            </set>
        </property>
        <!--设置null值-->
        <property name="girlFriends">
            <null/>
        </property>
        <!--给properties注入k-v-->
        <property name="info">
            <props>
                <prop key="学号">38</prop>
                <prop key="成绩">208</prop>
                <prop key="">8</prop>
            </props>
        </property>
    </bean>

    <bean id="address" class="com.zsh.pojo.Address">
        <property name="address" value="山东菏泽曹县"/>
    </bean>
</beans>

测试

@Test
public void test1(){
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

    Student student = (Student) context.getBean("student");

    System.out.println(student);
}

拓展注入

官方文档位置

img

pojo增加User类

package pojo;

public class User {
    private String name;
    private int id;
    public User() {
    }
    public User(String name, int id) {
        super();
        this.name = name;
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    @Override
    public String toString() {
        return "User [name=" + name + ", id=" + id + "]";
    }
}

注意: beans 里面加上这下面两行

使用p和c命名空间需要导入xml约束

xmlns:p=“http://www.springframework.org/schema/p”
xmlns:c=“http://www.springframework.org/schema/c”

使用方式

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

    <!--p命名空间注入/set注入,可以直接注入属性的值-》property-->
    <bean id="user" class="pojo.User" p:name="cxk" p:id="20" >
    </bean>

    <!--c命名空间,通过构造器注入,需要写入有参和无参构造方法-》construct-args-->
    <bean id="user2" class="pojo.User" c:name="cbh" c:id="22"></bean>
</beans>

测试

ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = context.getBean("user",User.class);//确定class对象,就不用再强转了
System.out.println(user.toString());

Bean作用域

image-20200802143342586

单例模式(默认)

<bean id="user2" class="pojo.User" c:name="cxk" c:age="19" scope="singleton"></bean>

img

弹幕评论:单例模式是把对象放在pool中,需要再取出来,使用的都是同一个对象实例

原型模式: 每次从容器中get的时候,都产生一个新对象!

<bean id="user2" class="pojo.User" c:name="cxk" c:age="19" scope="prototype"></bean>

img

其余的request、session、application这些只能在web开放中使用!

bean自动注入

需要在bean标签中加入auto属性

 <bean id="name" class="x.x.xxx.class" autowire=""/>

autowired可配置的属性

  1. byName

    需要注入的bean id 必须和 本类 中set方法的属性名一致;id唯一

    <bean class="com.zsh.pojo.Dog" id="dog"/>
    <bean class="com.zsh.pojo.Cat" id="cat"/>
    <bean id="people" class="com.zsh.pojo.People" autowire="byName">
        <property name="name" value="张三"/>
    </bean>
    
  2. byType

    根据bean类型来匹配,整个xml文件必须只有一个被匹配的bean,否则报错;class唯一

    <bean class="com.zsh.pojo.Dog" />
    <bean class="com.zsh.pojo.Cat" />
    <bean id="people" class="com.zsh.pojo.People" autowire="byType">
        <property name="name" value="张三"/>
    </bean>
    

@Autowired

xml文件中加入:

<context:annotation-config/>

(翻译:基于注释的配置的引入提出了一个问题,即这种方法是否比XML“更好”)

导入context约束
xmlns:context="http://www.springframework.org/schema/context"

配置注解的支持:< context:annotation-config/>
<?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>

默认是byType方式,如果匹配不上,就会byName

在属性上使用,也可以在set上使用

我们可以不用编写set方法了,前提是自动装配的属性在Spring容器里,且要符合ByName 自动装配

public class People {
    @Autowired
    private Cat cat;
    @Autowired
    private Dog dog;
    private String name;
}

@Nullable

字段标记了这个注解,说明该字段可以为空

public name(@Nullable String name){
}

如果定义了Autowire的require属性为false,说明这个对象可以为null,否则不允许为空(false表示找不到装配,不抛出异常)

//源码
public @interface Autowired { 
    boolean required() default true; 
}

@Autowired+@Qualifier

@Autowired不能唯一装配时,需要@Autowired+@Qualifier

如果@Autowired自动装配环境比较复杂。自动装配无法通过一个注解完成的时候,可以使用@Qualifier(value = “dog”)去配合使用,指定一个唯一的id对象

public class People {
    @Autowired
    private Cat cat;
    @Autowired
    @Qualifier(value = "dog")
    private Dog dog;
    private String name;
}

弹幕评论:

如果xml文件中 同一个 对象 被 多个bean 使用,Autowired无法按 类型 找到,可以用@Qualifier 指定id 查找

@Resource

默认以ByName的方式匹配,如果找不到匹配的bean id,则根据ByType的方式查找,如果类型又有多个,可以给Resource一个参数

指定id查找

public class People {
    Resource(name="cat")
    private Cat cat;
    Resource(name="dog")
    private Dog dog;
    private String name;
}
区别:

@Resource和@Autowired的区别:

  1. 都是用来自动装配的,都可以放在属性字段上
  2. @Autowired通过先byType后byName的方式实现,【常用】
  3. @Resource默认通过先byName后byType的方式实现,如果找不到名字,则通过byType实现!如果两个都找不到的情况下,就报错!【常用】
  4. 执行顺序不同:@Autowired通过byType的方式实现。@Resource默认通过byname的方式实现

@Component

意指作为组件被Spring扫描,等价于在xml文件中注册了bean

@Component
public class User {
    public String name ="猪鼻";
}

DAO【@Repository】

Service【@Service】

Controller【@Controller】

四个注解分别代表不同的层,但大意相同,代表将当前类注册到Spring中,装配bean

@Scope【作用域】

作用域

@Scope("指定为单例模式")
//    默认是单实例的
    /**
     * ConfigurableBeanFactory#SCOPE_PROTOTYPE  [prototype]
  	 * ConfigurableBeanFactory#SCOPE_SINGLETON  [singleton]
     * @return
     * prototype:多实例的: ioc容器启动并不会去调用方法创建对象放到IOC容器中
     * 每次获取的时候才会调用方法创建对象
     * singleton:单实例的(默认的): ioc容器启动会调用方法创建对象放到IOC容器中
     * 以后每次获取就是直接从容器(map.get())中拿
     * 懒加载
     *     单实例bean,默认在容器启动的时候创建对象
     *     懒加载:容器启动不创建对象,第一次使用(获取)Bean创建对象,并初始化
     */
//    @Scope("prototype")   //调整作用域
public class User{}

小结:

xml与注解:

  • xml更加万能,适用于所有场合,且维护简单方便
  • 注解中不是自己管理的类使用不了

xml与注解的最佳实践:

  • xml只用来管理bean
  • 注解只负责完成属性的注入
  • 记得开启注解的支持,和扫描包
<context:component-scan base-package="com.zsh.pojo"/>
<context:annotation-config/>

@Bean

@Bean(“getUser”)

有参数设置被扫描的bean名字

@Import

作用:

可以将两个配置类做为一个ioc容器

有参数可设置为被引入的配置类

@CompenentSacn

有参数指定扫描的包位置,扫描带有@Conponent的注解作为bean

@Configuration

配置类上加上这个注解表示:这个类作为配置文件,里面的@Bean注解相当于,加上@ComponentScan相当于在配置文件中开启了注解扫描

  1. Configuration的主要功能是用来注册Bean,下面是最简单的使用,注册一个Bean,然后使用这个Configuration注册/初始化Spring容器,然后就可以通过getBean来获取了

  2. 从源码能够看到,@Configuration上面有@Component注解,也就是说他其实也是一个Spring Bean,我们也可以通过getBean获取他,不过这个Bean有个特殊的地方是他的构造方法可以不使AutoWired注解(还可以注入Environment),就能自动隐式注入。

    @Configuration
    public class Main1 {
    
        public static void main(String[] args) {
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
            context.register(Main.class,Main1.class);
            context.refresh();
            Main.MyBean mybean = context.getBean(Main.MyBean.class);
            System.out.println(mybean);
    
            Main1 bean = context.getBean(Main1.class);
            System.out.println(bean.myBean);
            System.out.println(bean.environment);
        }
    
        public Main.MyBean myBean;
    
        public Environment environment;
    
        public Main1(Main.MyBean myBean, Environment environment){
            this.myBean = myBean;
            this.environment = 
                environment;
        }
    
    }
    

总结注解创建ioc容器的两种方式

前提:先在配置类中加入@Configuration注解

  1. 在配置类中定义一个方法,并使用@Bean标注

  2. 在需要作为bean的类上加入@Component注解,并在配置类上标注@ComponentScan(“需要被扫描的包”),

    这样会自动扫描带有@Component注解的类,作为bean

@Configuration VS @Component

  • 共同点:都可以用于创建Bean;
  • 不同点:实现原理不同,@Configuration基于CGlib代理实现,@Component基于反射实现;
  • 使用场景:@Configuration用于全局配置,比如数据库相关配置,MVC相关配置等;业务Bean的配置使用注解配置(@Component,@Service,@Repository,@Controller)。

代理模式

静态代理

  1. 代理模式主要做的事是帮被代理的类去处理一些繁琐、公共的业务,原来的类只需要专注自己的功能就好
  2. 实现业务分工
  3. 方便集中管理,因为每个功能都分离出来了
  4. 尽量使用多态的方式

缺点:一个角色就会产生一个代理,造成代码冗余

一个小demo

– 要实现的功能

public interface Rent {
    //接口方法
    public void rent();
}

– 实现功能的类

public class Host implements Rent {
    @Override
    public void rent() {
        System.out.println("房东出租房间");
    }
}

– 代理

public class Proxy implements Rent{
    private Rent rent;

    public Proxy() {
    }
    //  代理的类可以为原来的类做一些其他的工作
    public Proxy(Rent rent) {
        fee();
        this.rent = rent;
        signContract();
    }

    @Override
    public void rent() {
        rent.rent();
    }

    public void fee(){
        System.out.println("收取费用");
    }

    public void signContract(){
        System.out.println("签合同");
    }
}

– 测试

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

动态代理【jdk】

所有动态代理的底层都是反射

public class ProxyInvocationHandler implements InvocationHandler {

    /*
    一个万能的动态代理工具类,
    根据使用者传过来的被代理接口作为实现
     */
    private Object target;

    public void setRent(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //动态代理的本质就是使用反射机制实现
        //此处做一些代理外接繁琐功能的实现
         //根据传过来的接口,再根据参数去匹配对应的方法去执行
        Object result = method.invoke(target, args);
        return result;
    }


    //生成得到代理类
    public Object getProxy() throws Throwable {
        /*
        方法需要的参数
        ClassLoader loader 类加载器,生成的类作为target的实现
        类<?>[] interfaces 需要代理的接口,使用引入当前类再使用反射获取接口target
        InvocationHandler  处理器 当前类实现了 InvocationHandler 就是一个处理器
        * */
        return Proxy.newProxyInstance(target.getClass().getClassLoader()
                                      ,target.getClass().getInterfaces(), this);
    }
}
动态代理的优点
  • 有静态代理的所有优点
  • 一个动态代理,代理的是一个接口,一般就是 对应的 一类业务
  • 一个动态代理类可以代理多个类,只要是实现了同一个接口即可
博客解释

使用JDK的动态代理去生成代理只需要一行代码:

Moveable move = (Moveable) Proxy.newProxyInstance(Car.class.getClassLoader(), 
Car.class.getInterfaces(), new LogHandler(new Car()));

传入的参数中其实就俩,一是被代理类的类对象,二是自定义的增强处理代码。下面看下LogHandler源码:

public class LogHandler implements InvocationHandler{ 
    private Object target; 
     public LogHandler(Object object){
     super();
     this.target = object;
 }
 @Override
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //增强处理
 Object o = method.invoke(target,args);
 return o;
 }
}

  • 观察InvocationHandler实现类的源码可以发现,首先是定义了一个含参构造方法,该参数即为要代理的实例对象,观察target对象的使用位置在method.invoke()方法的参数中,不难看出,目的也就是为了执行目标方法。那重写的invoke()方法的三个参数又是什么呢?顾名思义,
    • proxy:动态生成的代理对象
    • method:目标方法的实例
    • args:目标方法的参数
    • 从Proxy.newProxyInstance()的方法参数中包含了被代理类的接口的类对象也不难得知,JDK的动态代理只能代理实现了接口的类, 没有实现接口的类不能实现动态代理。

关于Jdk动态代理的原理,我归纳得出四步

  1. 声明一段源码,源码动态产生动态代理
  2. 源码产生java文件,对java文件进行编译
  3. 得到编译生成后的class文件
  4. 把class文件load到内存之中,产生代理类对象返回即可。
cglib的动态代理

cglib使用动态代理的流程:

public static void main(String[] args) {
    Enhancer enhancer = new Enhancer();
    //设置被代理类
    enhancer.setSuperclass(Car.class);
    //设置回调函数
    enhancer.setCallback(new MethodInterceptor() { @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { //增强处理... //增强处理...
            Object o= proxy.invokeSuper(obj, args);//代理类调用父类的方法
            //增强处理...
            return o;
        }
    });
    //创建代理类并使用回调
    Car car = (Car) enhancer.create(); 
    //执行目标方法
    System.out.println(car.move());
}

从上面的代码看到,cglib的使用流程还是很清晰明了,各种参数顾名思义,和jdk的区别不大。生成的代理对象直接被该类引用,与我们认知的基于继承的动态代理没冲突。不过这种基于继承的方式就没有什么缺点吗?最明显的一点就是final修饰的类无法使用。

Spring用的啥

Jdk的动态代理和cglib的动态代理你都已经知晓,spring底层究竟是如何选择哪种动态代理方式,这也是面试经常考到的一个知识点。

标准回答如下:Spring会根据具体的Bean是否具有接口去选择动态代理方式,如果有接口,使用的是Jdk的动态代理方式,如果没有接口,使用的是cglib的动态代理方式。

AOP

什么是AOP

image-20200803134502169

AOP在Spring中使用

提供声明式事务,允许用户自定义切面

  • 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。

    日志,安全,缓存,事务等等…

  • 切面(Aspect):横切关注点 被模块化的特殊对象。即,它是一个类。(Log类)

  • 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。(Log类中的方法)

  • 目标(Target):被通知对象。(生成的代理类)

  • 代理(Proxy):向目标对象应用通知之后创建的对象。(生成的代理类)

  • 切入点(PointCut):切面通知执行的”地点”的定义。(最后两点:在哪个地方执行,比如:method.invoke())

  • 连接点(JointPoint):与切入点匹配的执行点。

image-20200803154043909

SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice【通知】:

image-20200803135937435

即AOP在不改变原有代码的情况下,去增加新的功能。(代理)

SpringAPI实现AOP

  1. 导入依赖
<dependency>
	<groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
	<version>1.9.5</version>
</dependency>
  1. 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
                           http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
  1. SpringAPI实现
//After实现后置通知接口
public class AfterLog implements AfterReturningAdvice {
    @Override
    public void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"执行完毕,返回参数:"+returnValue);
    }
}
//Before
public class Log implements MethodBeforeAdvice {

    /*
    method: 要执行的目标对象的方法
    args  : 参数,根据参数匹配方法
    target: 目标对象
     */
    @Override
    public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了.....");
    }
}
  1. applicationContext.xml【先看后面的execution表达式】
<!--注册bean-->
    <bean id="userservice" class="service.UserServiceImpl"/>
    <bean id="log" class="log.Log"/>
    <bean id="afterLog" class="log.AfterLog"/>


<!--方式1:使用原生API-->
<!--配置aop:需要导入约束-->
<aop:config>
     <!--切入点:expression:表达式,execution(要执行的位置)-->
     <!--UserServiceImpl.*(..) -》 UserServiceImpl类下的所以方法(参数)-->
    <aop:pointcut id="pointcut" expression="execution(* com.zsh.service.UserServiceImpl.*(..))"/>

    <!--给前置通知添加切入点-->
    <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
    <!--给后置通知添加切入点-->
    <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>

测试

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService service = context.getBean("userService", UserService.class);
service.add();

自定义切面实现AOP

public class DiyPointCut {
    public void before(){
        System.out.println("before");
    }
    public void after(){
        System.out.println("after");
    }
}

xml配置相关信息

<!--方式2:自定义-->
<bean id="diyLog" class="com.zsh.diy.DiyPointCut"/>
<aop:config>
    <!--
        定义切面
        ref:引用的切面类
        -->
    <aop:aspect ref="diyLog">
        <!--定义切入点-->
        <aop:pointcut id="point" expression="execution(* com.zsh.service.UserServiceImpl.*(..)) "/>
        <!--前置通知-->
        <aop:before method="before" pointcut-ref="point"/>
        <!--后置通知 returning:返回值-->
        <aop:after-returning method="after" pointcut-ref="point" />
    </aop:aspect>
</aop:config>

测试

@Test
public void test1(){

    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

    UserService service = context.getBean("userService", UserService.class);

    service.add();

}

注解实现AOP

创建切面类

@Aspect
public class AnnotationPointCut  {
    @Before("execution(* com.zsh.service.UserServiceImpl.*(..))")
    public void Before(){
        System.out.println("方法执行前......");
    }
    @After("execution(* com.zsh.service.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("方法执行后......");
    }

    //在增强环绕中,可以给定义一个参数【ProceedingJoinPoint】,代表要处理的切入点类
    @Around("execution(* com.zsh.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("方法环绕前");
        //执行方法
        Object proceed = jp.proceed();
        System.out.println("方环绕后");
    }
}

xml配置

<!--方式3:注解实现AOP-->
<bean id="annotationPointCut" class="com.zsh.diy.AnnotationPointCut"/>
<!--
    开启自动代理
    实现方式
    默认JDK (proxy-targer-class="fasle")
    cgbin (proxy-targer-class="true")
    -->
<aop:aspectj-autoproxy/>

测试

@Test
public void test1(){

    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

    UserService service = context.getBean("userService", UserService.class);

    service.add();

}

Spring-Mybatis

spring整合mybatis【方式1】

官方文档:https://mybatis.org/spring/zh/【能看懂!】

应该使用SqlSessionTemplate因为线程安全,可重用性高

**===============================================================**

导入jar包和扫描xml文件

<dependencies>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.9</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.7</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.4</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.2</version>
        </dependency>

    </dependencies>
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>

demo结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XKZgByST-1624634180530)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210525191915522.png)]

编写顺序:
User -> UserMapper -> UserMapper.xml -> spring-dao.xml -> UserServiceImpl -> applicationContext.xml -> MyTest

实体类:

对应mysql表

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Integer id;
    private String name;
    private String pwd;
}

UserMapper

public interface UserMapper {
    public List<User> queryAll();
}

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--命名空间要写全限定路径-->
<mapper namespace="com.zsh.mapper.UserMapper">
    <select id="queryAll" resultType="user">
        SELECT * FROM mybatis.user
    </select>

</mapper>

【复用死代码】

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


    <!--使用Spring提供的jdbc:org.springframework.jdbc.DataSource-->
    <!--设置数据源-->
    <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <!--用户名-->
        <property name="username" value="root"/>
        <!--密码-->
        <property name="password" value="123456"/>
        <!--数据库url-->
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
        <!--数据库连接驱动-->
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    </bean>

    <!--SqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--mybatis-config的所有配置都能在这里实现-->
        <!--指定数据源-->
        <property name="dataSource" ref="datasource" />
        <!--引用mybatis-config,也可以不引用,这里可以配置所有在mybatis配置过的信息-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <!--指定接口与数据库交互的Mapper.xml文件和引用的mybatis文件不能有相同的配置-->
        <property name="mapperLocations" value="classpath:com/zsh/mapper/*.xml"/>
    </bean>

    <!--通过注入一个SqlSessionFactory来创建sqlSession-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!--因为SqlSessionTemplate没有Set方法,所以只能以构造方法注入-->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>

    <!--真正执行操作的类,注入属性sqlSession-->
    <bean id="userMapper" class="com.zsh.mapper.UserMapperImpl">
        <property name="sqlSession" ref="sqlSession"/>
    </bean>
</beans>

mybatis-config

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration >
    <typeAliases>
        <package name="com.zsh.pojo"/>
    </typeAliases>

    <mappers>
        <!--Spring和mybatis-config中相同的配置不能同时存在-->
        <!--<mapper class="com.zsh.mapper.UserMapper"/>-->
        <!--<package name="com.zsh.mapper"/>-->
    </mappers>
</configuration>

SqlSessionTemplate

与sqlSession作用相同,SqlSessionTemplate线程安全,且spring推荐使用

private SqlSessionTemplate sqlSession;

public void setSqlSession(SqlSessionTemplate sqlSession) {
    this.sqlSession = sqlSession;
}

@Override
public List<User> queryAll() {
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> list = mapper.queryAll();
    return list;
}

测试

@Test
public void test1() throws IOException {
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
    UserMapper mapper = context.getBean("userMapper", UserMapper.class);
    List<User> list = mapper.queryAll();
    for (User user : list) {
        System.out.println(user);
    }
}

spring整合mybatis【方式2】

使用接口实现类测试

//通过继承SqlSessionDaoSupport 来代理获得SqlSession
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
    @Override
    public List<User> queryAll() {
        SqlSession sqlSession = getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return  mapper.queryAll();
    }
}

引用前面xml文件中的SqlSessionFactory来创建SqlSession实例

<import resource="spring-dao.xml"/>
<!--
    继承SqlSessionDaoSupport之后需要注入一个sqlSessionFactory
    作为创建SqlSessionTemplate的条件
    省略了创建sqlSession的步骤
    -->
<bean id="userMapper2" class="com.zsh.mapper.UserMapperImpl2">
    <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>

测试

@Test
public void test1() throws IOException {
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserMapper mapper = context.getBean("userMapper2", UserMapper.class);
    for (User user : mapper.queryAll()) {
        System.out.println(user);
    }
}

execution表达式

execution(修饰符  返回值  包名.类名/接口名.方法名(参数列表))注意老师忽略掉修饰符了 自己可以写上修饰符试试
(..)可以代表所有参数,(*)代表一个参数,(*,String)代表第一个参数为任何值,第二个参数为String类型.

<aop:aspect ref="diyLog">
	<aop:pointcut id="point" expression="execution(* com.zsh.service.UserServiceImpl.*(..)) "/>
</aop:aspect>

Spring事务

  • 把一组业务当成一个业务来做;要么都成功,要么都失败!
  • 事务在项目开发中,十分的重要,涉及到数据的一致性问题
  • 确保完整性和一致性

事务的ACID原则:
1、原子性
2、隔离性
3、一致性
4、持久性

ACID参考文章:https://www.cnblogs.com/malaikuangren/archive/2012/04/06/2434760.html

Spring中的事务管理

有两种方式

  • 声明式事务:AOP
  • 编程式事务:需要再代码中,进行事务管理

声明式事务【方式1;推荐】

1. 导入依赖/jar包
<dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.9</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.7</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.4</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.2</version>
        </dependency>

    </dependencies>
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>
2.spring-dao.xml【重点】

请认真阅读注释理解

<?xml version="1.0" encoding="UTF-8"?>
       <!--这里需要导入命名空间 tx!!!-->
<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"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           https://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/tx
                           https://www.springframework.org/schema/tx/spring-tx.xsd
                           http://www.springframework.org/schema/aop
                           https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--使用Spring提供的jdbc:org.springframework.jdbc.DataSource-->
    <!--设置数据源-->
    <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <!--用户名-->
        <property name="username" value="root"/>
        <!--密码-->
        <property name="password" value="123456"/>
        <!--数据库url-->
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
        <!--数据库连接驱动-->
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    </bean>

    <!--SqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--mybatis-config的所有配置都能在这里实现-->
        <!--指定数据源-->
        <property name="dataSource" ref="datasource" />
        <!--引用mybatis-config-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <!--指定接口与数据库交互的Mapper.xml文件-->
        <property name="mapperLocations" value="classpath:com/zsh/mapper/*.xml"/>
    </bean>

    <!--通过注入一个SqlSessionFactory来创建sqlSession-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!--因为SqlSessionTemplate没有Set方法,所以只能以构造方法注入-->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>
========================================================================================
    <!--配置事务管理器,声明式事务-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--配置dataSource-->
        <property name="dataSource" ref="datasource"/>
    </bean>

    <!--结合aop实现事务织入-->
    <!--配置事务的通知类-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--配置事务的传播特性 propagation-->
        <tx:attributes>
            <!--指定扫描的方法-->
            <!--<tx:method name="insert" propagation="REQUIRED"/>-->
            <!--让select方法只读数据-->
            <tx:method name="query" read-only="true"/>
            <!--可以直接扫描所有方法,添加事务-->
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

    <!--配合上面的事务切入-->
    <aop:config>
        <!--设置切入点-->
        <aop:pointcut id="txPointCut" expression="execution(* com.zsh.mapper.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
    </aop:config>
</beans>
mybatis-config.xml

它的存在代表这是一个整合了mybatis的项目^^

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration >
    <typeAliases>
        <package name="com.zsh.pojo"/>
    </typeAliases>

    <mappers>
        <!--Spring和mybatis-config中相同的配置不能同时存在-->
        <!--<mapper class="com.zsh.mapper.UserMapper"/>-->
        <!--<package name="com.zsh.mapper"/>-->
    </mappers>
</configuration>
搭建demo结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AvMb0KNn-1624634180533)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210526104413968.png)]

User实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Integer id;
    private String name;
    private String pwd;
}
UserMapper接口
public interface UserMapper {
    //    给定三个方法用于测试
    public int insertUser(User user);
    public int deleteUserById(Integer id);
    public List<User> queryAll();
}
UserMapperImpl实现类
public class UserMapperImpl implements UserMapper {
    private SqlSessionTemplate sqlSession;
    
    public void setSqlSession(SqlSessionTemplate sqlSession) {
        this.sqlSession = sqlSession;
    }
    @Override
    public int insertUser(User user) {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.insertUser(user);
    }

    @Override
    public int deleteUserById(Integer id) {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.deleteUserById(id);
    }

    @Override
    public List<User> queryAll() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = new User(null, "老八", "13213");
        insertUser(user);
        deleteUserById(7);
        List<User> list = mapper.queryAll();
        return list;
    }
}
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--命名空间要写全限定路径-->
<mapper namespace="com.zsh.mapper.UserMapper">
    <select id="queryAll" resultType="user">
        SELECT * FROM mybatis.user
    </select>

    <insert id="insertUser" parameterType="user" >
        INSERT INTO mybatis.user(id,name,pwd) VALUES(#{id},#{name},#{pwd})
    </insert>

    <delete id="deleteUserById" parameterType="int" >
        DELETE FROM mybatis.user WHERE id = #{id}
    </delete>

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

    <import resource="spring-dao.xml"/>

    <bean id="userMapper" class="com.zsh.mapper.UserMapperImpl">
        <property name="sqlSession" ref="sqlSession"/>
    </bean>

</beans>
测试
public class MyTest {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserMapper mapper = context.getBean("userMapper", UserMapper.class);
        for (User user : mapper.queryAll()) {
            System.out.println(user);
        }
    }
}

小结

声明式事务配合切面来完成事务的构建,在不改动原来代码的情况加添加事务功能,
增删改执行过程中出现异常会回滚数据,从而保证数据的ACID

spring-dao.xml【仔细阅读】

编码式事务【方式2】

通过在增删改的代码之前添加开启事务的代码,并且在try cache.....语句中捕捉异常,回滚事务
这个方式也可以,但是只建议在项目构建之处使用,避免修改源码的问题

思考:
为什么需要事务?

  • 如果不配置事务,可能存在数据提交不一致的情况下;
  • 如果不在spring中去配置声明式事务,我们就需要在代码中手动配置事务!
  • 事务在项目的开发中非常重要,涉及到数据的一致性和完整性问题!

Spring项目基本操作

​ 创建Spring的容器对象是根据Spring配置文件所在的位置,使用接口来调用不同的实现类

​ 如果是类路径(classpath),使用ClassPathApplicationContext();

​ 如果是项目根目录,使用FileSystemXmlApplicationContext();

bean对象的作用域,作用域是对象的存在范围和可见性

​ 1、单例: singleton ,bean对象默认为单例,表示叫这个名称的对象在Spring容器中只有一个

​ 指定单例的语法:

​ 创建容器时,会创建所有单例对象

​ 2、DI实现有两种语法:

​ 1)、使用xml文件中的标签和属性,

​ 2)、使用注解

​ Di的分类:

​ 1)、设值注入

​ 简单类型的设值注入:就是Spring调用实体类的set方法,完成赋值,最常用的方式

​ 引用类型的设值注入:1)、ref作为属性 2)、ref作为子标签 。新建一个bean作为需要被赋值的 ref 内容

​ 2)、构造注入

​ Spring去调用实体类的有参数构造方法,完成属性赋值。使用较少

​ 简单类型的设值注入:

​ 1、,每个

​ 标签只能为一个属性赋值,在标签中生效。

​ 如果需要同时给多个属性赋值,需要编写多个标签

​ 2、使用xml注入赋值时,Spring关系的只有set方法。赋值时只要property中的属性能在实体类中找到对应的 set 方法,不管有没有sex这个成员变量(属性),不会违反Spring的约定。

​ 3)、自动注入(autoWrite):

​ ByName(按属性名注入):

​ 类中的属性名称与配置文件(xml)中bean标签的id一致且数据类型一致,在bean上添加autoWrite

​ ByType(按类型注入):

​ java类中引用数据的类型和spring容器(xml配置文件)中的class属性是同源关系的,这样的bean能够赋 值给引用数据类型

​ 同源关系:

​ 1、java类中引用数据的类型和中class值是一样的。

​ 2、java类中引用数据的类型和中class值是父子类关系。

​ 3、接口和对应的实现类

注意:xml配置文件中如果使用ByType作为为属性值的话,符合条件的同源bean只能有一个。

​ 4)xml文件和注解对比优缺点:

​ xml优点:与源代码完全分离,就算日后业务需求发生改变,只需修改xml文件的的属性和配置,不需要修改源代码

​ 用配置文件还可以创建别人提供的类对象

​ xml缺点:代码繁多,需要别写很多个配置。稍复杂。

​ 需要翻阅配置文件才能知道属性的赋值和创建出来的对象的名字,不够清晰和直观

​ 注解的优点:方便快捷,有提示。开发效率高

​ 只需要观看源代码就可以知道相关的信息

​ 注解的缺点:如果需求发生改变,需要频繁修改源代码。

​ 源代码已修改,就需要重新编译,然后替换服务器中的class文件

​ 在代码中加入多个注解,代码稍显杂乱,不美观

​ 5)动态代理:

​ JDK的动态代理:要求是目标类必须实现接口
​ JAVA通过java.lang.reflect包提供的三个类支持代理模式:proxy、method 和 InvokeactionHandler

​ Proxy:通过proxy.newProxyInstance()创建目标对象

​ Method:调用method.invoke(taget ,args)执行目标方法

​ InvokeactionHandler接口:在接口的实现类中,重写invoke(),实现功能的增强

​ cglib的动态代理:

​ 是第三方的工具库。能够生成代理的对象

​ cglib对象创建原理:继承。cglib能够创建目标类的子类,在子类中重写方法,实现功能的增强。

y" scope=“singleton”>

​ 创建容器时,会创建所有单例对象

​ 2、DI实现有两种语法:

​ 1)、使用xml文件中的标签和属性,

​ 2)、使用注解

​ Di的分类:

​ 1)、设值注入

​ 简单类型的设值注入:就是Spring调用实体类的set方法,完成赋值,最常用的方式

​ 引用类型的设值注入:1)、ref作为属性 2)、ref作为子标签 。新建一个bean作为需要被赋值的 ref 内容

​ 2)、构造注入

​ Spring去调用实体类的有参数构造方法,完成属性赋值。使用较少

​ 简单类型的设值注入:

​ 1、,每个

​ 标签只能为一个属性赋值,在标签中生效。

​ 如果需要同时给多个属性赋值,需要编写多个标签

​ 2、使用xml注入赋值时,Spring关系的只有set方法。赋值时只要property中的属性能在实体类中找到对应的 set 方法,不管有没有sex这个成员变量(属性),不会违反Spring的约定。

​ 3)、自动注入(autoWrite):

​ ByName(按属性名注入):

​ 类中的属性名称与配置文件(xml)中bean标签的id一致且数据类型一致,在bean上添加autoWrite

​ ByType(按类型注入):

​ java类中引用数据的类型和spring容器(xml配置文件)中的class属性是同源关系的,这样的bean能够赋 值给引用数据类型

​ 同源关系:

​ 1、java类中引用数据的类型和中class值是一样的。

​ 2、java类中引用数据的类型和中class值是父子类关系。

​ 3、接口和对应的实现类

注意:xml配置文件中如果使用ByType作为为属性值的话,符合条件的同源bean只能有一个。

​ 4)xml文件和注解对比优缺点:

​ xml优点:与源代码完全分离,就算日后业务需求发生改变,只需修改xml文件的的属性和配置,不需要修改源代码

​ 用配置文件还可以创建别人提供的类对象

​ xml缺点:代码繁多,需要别写很多个配置。稍复杂。

​ 需要翻阅配置文件才能知道属性的赋值和创建出来的对象的名字,不够清晰和直观

​ 注解的优点:方便快捷,有提示。开发效率高

​ 只需要观看源代码就可以知道相关的信息

​ 注解的缺点:如果需求发生改变,需要频繁修改源代码。

​ 源代码已修改,就需要重新编译,然后替换服务器中的class文件

​ 在代码中加入多个注解,代码稍显杂乱,不美观

​ 5)动态代理:

​ JDK的动态代理:要求是目标类必须实现接口
​ JAVA通过java.lang.reflect包提供的三个类支持代理模式:proxy、method 和 InvokeactionHandler

​ Proxy:通过proxy.newProxyInstance()创建目标对象

​ Method:调用method.invoke(taget ,args)执行目标方法

​ InvokeactionHandler接口:在接口的实现类中,重写invoke(),实现功能的增强

​ cglib的动态代理:

​ 是第三方的工具库。能够生成代理的对象

​ cglib对象创建原理:继承。cglib能够创建目标类的子类,在子类中重写方法,实现功能的增强。

​ cglib的使用要求:目标类不能是final,目标方法不能是final

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值