Spring个人笔记

断了今生、忘了曾经 提交于 2020-02-29 21:04:17

xml配置

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

    <bean id="..." class="...">  
        <!-- collaborators and configuration for this bean go here -->
    </bean>

    <bean id="..." class="...">
        <!-- collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions go here -->

</beans>

其中id是bean字符串,bean的唯一标识符,相当于对象名,class是bean类名的完全限定路径

2.别名

起别名有两种方式,

1.通过alias

<bean id="helloWorld" class="com.hyq.pojo.HelloWorld">
        <property name="hello" value="abc"/>
    </bean>
    <alias name="helloWorld" alias="dsd"/>
name=hello是创建对象的变量名,相当于Helloworld hello= new Helloworld();
value 为参数赋值,
ApplicationContext context = new                       ClassPathXmlApplicationContext("ApplicationContext.xml");解析xml文件的固定格式(不止一种)
        HelloWorld helloWorld = (HelloWorld) context.getBean("helloWorld");获取类对象,
        HelloWorld helloWorld = (HelloWorld) context.getBean("dsd");也可以用别名获取对象

2.通过bean中的name属性

<bean id="helloWorld" class="com.hyq.pojo.HelloWorld" name="dsdd,h1">
        <property name="hello" value="abc"/>
    </bean>
ApplicationContext context = new                       ClassPathXmlApplicationContext("ApplicationContext.xml");解析xml文件的固定格式(不止一种)
        HelloWorld helloWorld = (HelloWorld) context.getBean("helloWorld");获取类对象,
        HelloWorld helloWorld = (HelloWorld) context.getBean("h1");也可以用别名获取对象

用bean中name更高级,可以起多个别名

IoC创建对象方式

1.通过无参构造来创建对象,系统默认

2.通过有参构造函数来创建。(构造器construction )

​ 1.下标赋值

<bean id="helloWorld" class="com.hyq.pojo.HelloWorld">
        <constructor-arg index="0" value="abc"/>
    </bean>
index:下标是从有参构造函数中括号的参数开始算起

​ 2.类型赋值(不建议使用)

<bean id="helloWorld" class="com.hyq.pojo.HelloWorld">
        <constructor-arg type="java.lang.String" value="ds"/>
    </bean>
当有参构造函数括号中有两个string类型的变量时,无法使用

总结:在xml注册的bean对象,只要注册,无论使不使用,对象都被创建

DI依赖注入

分为以下几种

Student实体类

package com.hyq.pojo;

import java.util.*;

public class Student {
    private String name;
    private Address address;
    private String[] array;
    private List<String> hobby;
    private Map<String,String> books;
    private Set<String> movie;
    private Properties properties;
    private String wife;

    public Student() {
    }

    public Student(String name,String wife) {
        this.name = name;
        this.wife = wife;
    }

    public String getName() {
        return name;
    }

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

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public String[] getArray() {
        return array;
    }

    public void setArray(String[] array) {
        this.array = array;
    }

    public List<String> getHobby() {
        return hobby;
    }

    public void setHobby(List<String> hobby) {
        this.hobby = hobby;
    }

    public Map<String, String> getBooks() {
        return books;
    }

    public void setBooks(Map<String, String> books) {
        this.books = books;
    }

    public Set<String> getMovie() {
        return movie;
    }

    public void setMovie(Set<String> movie) {
        this.movie = movie;
    }

    public Properties getProperties() {
        return properties;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    public String getWife() {
        return wife;
    }

    public void setWife(String wife) {
        this.wife = wife;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", address=" + address +
                ", array=" + Arrays.toString(array) +
                ", hobby=" + hobby +
                ", books=" + books +
                ", movie=" + movie +
                ", properties=" + properties +
                ", wife='" + wife + '\'' +
                '}';
    }
}

Address实体类

package com.hyq.pojo;

public class Address {

    private String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Address{" +
                "address='" + address + '\'' +
                '}';
    }
}

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

    <bean id="address" class="com.hyq.pojo.Address">
        <property name="address" value="河南"/>
    </bean>

    <bean id="student" class="com.hyq.pojo.Student">
        <!-- 普通注册 -->
        <property name="name" value="小明"/>
        <!-- bean注册 -->
        <property name="address" ref="address"/>
        <!-- 数组注册 -->
        <property name="array" >
            <array>
                <value>00</value>
                <value>11</value>
                <value>22</value>
            </array>
        </property>
        <!-- map注册 -->
        <property name="books">
            <map>
                <entry key="a" value="语文"/>
                <entry key="b" value="数学"/>
                <entry key="c" value="英语"/>
            </map>
        </property>
        <!-- list注册 -->
        <property name="hobby">
            <list>
                <value>音乐</value>
                <value>美术</value>
                <value>电影</value>
            </list>
        </property>
        <!-- set注册 -->
        <property name="movie">
            <set>
                <value>中国电影</value>
                <value>美国电影</value>
                <value>印度电影</value>
            </set>
        </property>
        <!-- property注册 -->
        <property name="properties">
            <props>
                <prop key="driver">com.mysql</prop>
                <prop key="user">root</prop>
                <prop key="password">123</prop>
            </props>
        </property>
        <!-- null注册 -->
        <property name="wife">
            <null />
        </property>
    </bean>

</beans>

P命名空间注册需要导入约束信息:xmlns:p="http://www.springframework.org/schema/p"

c命名空间注册需要导入约束信息:xmlns:c="http://www.springframework.org/schema/c"

引入后的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: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">

    <bean id="student" class="com.hyq.pojo.Student"
          p:name="xiaoHu"/>
    <bean id="student1" class="com.hyq.pojo.Student"
          c:name="a"
          c:wife="0"/>

</beans>

测试类

public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext1.xml");
        Student student =  context.getBean("student",Student.class);student--->student1
        System.out.println(student);
    }

总结:C命名空间是基于有参构造器,P命名空间是基于property

bean的自动装配

不使用自动装配的配置文件如下:

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

   <bean id="cat" class="com.hyq.pojo.Cat"/>
    <bean id="dog" class="com.hyq.pojo.Dog"/>

    <bean id="people" class="com.hyq.pojo.People">
        <property name="name" value="低调"/>
        <property name="cat" ref="cat"/>
        <property name="dog" ref="dog"/>
    </bean>
</beans>

dog类如下:

public class Dog {
    public void eat(){
        System.out.println("吃狗粮");
    }
}

cat类如下:

public class Cat {
    public void eat(){
        System.out.println("吃猫粮");
    }
}

people类如下:

package com.hyq.pojo;

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

    public String getName() {
        return name;
    }

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

    public Cat getCat() {
        return cat;
    }

    public void setCat(Cat cat) {
        this.cat = cat;
    }

    public Dog getDog() {
        return dog;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }

}

1.使用byName的配置文件如下:

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

   <bean id="cat" class="com.hyq.pojo.Cat"/>
    <bean id="dog" class="com.hyq.pojo.Dog"/>

    <bean id="people" class="com.hyq.pojo.People" autowire="byName">
        <property name="name" value="低调"/>
    </bean>
</beans>
在bean中有autowire的属性,byName的原理是<bean id="dog" class="com.hyq.pojo.Dog"/>中的id必须唯一,且这个id必须和com.hyq.pojo.Dog类中setDog对应,不能写成setDog1.需要一 一对应。

2.使用byType的配置文件如下:

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

   <bean id="cat" class="com.hyq.pojo.Cat"/>
    <bean id="dog" class="com.hyq.pojo.Dog"/>

    <bean id="people" class="com.hyq.pojo.People" autowire="byType">
        <property name="name" value="低调"/>
    </bean>
</beans>
原理:在<bean id="dog" class="com.hyq.pojo.Dog"/>中,class必须唯一,不能重复,也就说该类只能用一次,和 id没有关系,可以省略不写。

3.注解实现自动装配

使用注解必须:

1.导入约束,

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

此时people中的字段cat和dog可以使用注解配置,就不用在xml中使用byName或者byType了。

    @Autowired
    private Cat cat;
    @Autowired
    private Dog dog;
使用了注解配置,只需要在字段的上方加上@autowired即可。
里面的set方法也可以不写。

4.补充:

字段中如果有@Nullable,说明这个字段可以为空

<context:component-scan base-package="com.hyq.pojo"/>
component:成分;scan:扫描
可以扫描指定包下的注解

使用注解开发

== 在spring4之后,要使用注解开发,必须要有aop的包 ==

1.component-scan,属性注入

在xml中使用时,可以简化配置,只需要在其他类名上标注@Component即可,如下:

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"
       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.hyq.pojo"/>
    <context:annotation-config/>

</beans>

User类如下:

package com.hyq.pojo;

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

//此处使用了注解配置,相当于 <bean id="user" class="com.hyq.pojo.User"/>
@Component
public class User {
    //此处使用了注解配置,可以简化name的赋值,相当于<property name="name" value="张三"/>
    @Value("张三")
    private String name;

    public String getName() {
        return name;
    }
}

2.动态代理

想实现某个接口,你需要写一个类,然后在类名字的后面给出“implements”XXX接口。这才是实现某个接口:

public interface MyInterface {
  void fun1();
  void fun2();
}
public class MyInterfaceImpl implements MyInterface {
  public void fun1() {
    System.out.println("fun1()");
  }
  public void fun2() {
    System.out.println("fun2()");
  }
}

动态代理技术可以通过一个方法调用就可以生成一个对指定接口的实现类对象。

Class[] cs = {MyInterface.class};
MyInterface mi = (MyInterface)Proxy.newProxyInstance(loader, cs, h);

​ 上面代码中,Proxy类的静态方法newProxyInstance()方法生成了一个对象,这个对象实现了cs数组中指定的接口。没错,返回值mi是MyInterface接口的实现类。你不要问这个类是哪个类,你只需要知道mi是MyInterface接口的实现类就可以了。你现在也不用去管loader和h这两个参数是什么东东,你只需要知道,Proxy类的静态方法newProxyInstance()方法返回的方法是==实现了指定接口的实现类对象==,甚至你都没有看见实现类的代码。  

​ 动态代理就是在运行时生成一个类,这个类会实现你指定的一组接口,而这个类没有.java文件,是在运行时生成的,你也不用去关心它是什么类型的,你只需要知道它实现了哪些接口即可。

newProxyInstance()方法的参数
  Proxy类的newInstance()方法有三个参数:
    ClassLoader loader:它是类加载器类型,你不用去理睬它,你只需要知道怎么可以获得它就可以了:MyInterface.class.getClassLoader()就可以获取到ClassLoader对象,没错,只要你有一个Class对象就可以获取到ClassLoader对象;
    Class[] interfaces:==指定newProxyInstance()方法返回的对象要实现哪些接口==,没错,可以指定多个接口,例如上面例子只我们只指定了一个接口:Class[] cs = {MyInterface.class};
    InvocationHandler h:它是最重要的一个参数!它是一个接口!它的名字叫调用处理器!你想一想,上面例子中mi对象是MyInterface接口的实现类对象,那么它一定是可以调用fun1()和fun2()方法了,难道你不想调用一下fun1()和fun2()方法么,它会执行些什么东东呢?其实无论你调用代理对象的什么方法,它都是在调用InvocationHandler的invoke()方法!

InvocationHandler的invoke()方法   

InvocationHandler的invoke()方法的参数有三个:

Object proxy:代理对象,也就是Proxy.newProxyInstance()方法返回的对象,通常我们用不上它;   Method method:表示当前被调用方法的反射对象,例如mi.fun1(),那么method就是fun1()方法的反射对象;Object[] args:表示当前被调用方法的参数,当然mi.fun1()这个调用是没有参数的,所以args是一个零长数组。

Object o = method.invoke(obj, args);

method是方法对象

obj是该方法对象所在的类对象实例

args是方法参数

返回是Object类型,因为编译时无法获得方法轻易获得所要调用方法的返回类型,因此使用了“万能型”  

最后要说的是invoke()方法的返回值为Object类型,它表示当前被调用的方法的返回值,当然mi.fun1()方法是没有返回值的,所以invoke()返回的就必须是null了。

实例1:

public interface Waiter {
  public void serve();
}
public class MyWaiter implements Waiter {
  public void serve() {
    System.out.println("服务...");
  }
}

  现在我们要对MyWaiter对象进行增强,要让它在服务之前以及服务之后添加礼貌用语,即在服务之前说“您好!”,在服务之后说:“很高兴为您服务!”。

public class MainApp1 {
  public static void main(String[] args) {
    ClassLoader loader = MainApp1.class.getClassLoader();
    Class[] cs = {Waiter.class};
    Waiter target = new MyWaiter();
    MyInvocationHandler h = new MyInvocationHandler(target);
    Waiter waiter = (Waiter)Proxy.newProxyInstance(loader, cs, h);
    waiter.serve();
  }
}
class MyInvocationHandler implements InvocationHandler {
  public Waiter target;
  public MyInvocationHandler(Waiter target) {
    this.target = target;
  }
  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

public interface Rent {
    public void rent();
}
public class Host implements Rent {
    public void rent() {
        System.out.println("房东出租房子");
    }
}
public class ProxyDeal implements InvocationHandler {
    private Rent rent;

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

    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                rent.getClass().getInterfaces(),this);
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("前");
        Object invoke = method.invoke(rent, args);
        System.out.println("后");
        return invoke;
    }
}
public void test(){
        Rent rent = new Host();
        ProxyDeal proxyDeal = new ProxyDeal();
        proxyDeal.setRent(rent);
        Object proxy = proxyDeal.getProxy();
        Rent rent1 = (Rent)proxy;
        rent1.rent();

    }

AOP面向切面编程

AOP的相关术语及应用

  • Aspect:表示切面。切入业务流程的一个独立模块 。实际上是若干个====

  • join point:表示连接点。是业务流程在运行过程中需要插入切面的具体位置。

  • Advice: 表示通知。是切面的具体实现方法。==它是类中的一个方法== 可分为

    • 前置通知(Before)

    • 后置通知(AfterRunning)

    • 异常通知(AfterThrowing)

    • 最终通知(After)

    • 环绕通知(Around)

      实现方法具体属于哪类通知,是在配置文件和注解中指定的。

  • Pointcut :表示切入点。定义通知应该切入到哪些连接点上,不同的通知通常需要切入到不同的连接点上

  • Target: 表示目标对象。被一个或者多个切面所通知的对象。

  • Proxy :表示代理对象。将通知应用到目标对象之后被动态创建的对象。可以简单地理解为,代理对象为目标对象的业务逻辑功能加上被切入的切面所形成的对象。

  • Weaving :表示切入,也称为织入。将切面应用到目标对象从而创建一个新的代理对象的过程。这个过程可以发生在编译期、类装载期及运行期。

原生方式实现

User接口和UserImpl类位于Dao包下

public interface User {
    public void add();
    public void delete();
    public void update();
    public void query();
}
public class UserImpl implements User {
    public void add() {
        System.out.println("增加了一个用户");
    }

    public void delete() {
        System.out.println("删除了一个用户");
    }

    public void update() {
        System.out.println("修改了一个用户");
    }

    public void query() {
        System.out.println("查询了一个用户");
    }
}

AfterLog和BeforeLog位于log包下

public class AfterLog implements AfterReturningAdvice {
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("执行了"+method.getName()+"的方法");
    }
}
public class BeforeLog implements MethodBeforeAdvice {
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了"+method.getName()+"的方法,");
    }
}

注册类

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

<!--    注册bean   -->
    <bean id="userImpl" class="com.hyq.dao.UserImpl"/>
    <bean id="afterLog" class="com.hyq.log.AfterLog"/>
    <bean id="beforeLog" class="com.hyq.log.BeforeLog"/>

<!--    切入点   -->
    <aop:config>
        execution(切入的指定位置)
        <aop:pointcut id="pointcut" expression="execution(* com.hyq.dao.UserImpl.*(..))"/>
        
        <aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>

    </aop:config>
</beans>

pointcut:切入点,要插入到指定的位置。

execution语法:

其中访问修饰符可以省略

advice:通知

引用advice-ref:的类切入到pointcut-ref:这个切入点。

测试类:

public class MyTest {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext(
                "applicationContext.xml");
        User user = (User)context.getBean("userImpl");
        user.add();
    }
}

输出结果:

​ 执行了add的方法,
增加了一个用户
执行了add的方法

自定义类来实现AOP

diyPointCut类:

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

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

<!--    注册bean   -->
    <bean id="userImpl" class="com.hyq.dao.UserImpl"/>
    <bean id="afterLog" class="com.hyq.log.AfterLog"/>
    <bean id="beforeLog" class="com.hyq.log.BeforeLog"/>

<!--    方式二:自定义类实现-->
<!--    注册Bean-->
    <bean id="diyPointCut" class="com.hyq.diy.DiyPointCut"/>

    <aop:config>
<!--        切面-->
        <aop:aspect ref="diyPointCut">
<!--            切入点-->
            <aop:pointcut id="pointcut" expression="execution(* com.hyq.dao.UserImpl.*(..))"/>
<!--            通知          -->
            <aop:before method="before" pointcut-ref="pointcut"/>
            <aop:after method="after" pointcut-ref="pointcut"/>

        </aop:aspect>
    </aop:config>
</beans>

ref:指向指定类

注解实现AOP

Annotation类

@Aspect//这是一个切面
public class Annotation {
    //切入点
    @Before("execution(* com.hyq.dao.UserImpl.*(..))")
    public void before(){
        System.out.println("----前----");
    }
}
<?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">

<!--    注册bean   -->
    <bean id="userImpl" class="com.hyq.dao.UserImpl"/>
    <bean id="afterLog" class="com.hyq.log.AfterLog"/>
    <bean id="beforeLog" class="com.hyq.log.BeforeLog"/>

<!--    方式三-->
    <bean id="annotation" class="com.hyq.annotationAop.Annotation"/>

<!--    开启注解-->
    <aop:aspectj-autoproxy/>
</beans>

==注意:==使用注解需要开启注解。

<!--    开启注解-->
    <aop:aspectj-autoproxy/>
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!