Spring基础

1. Spring

1.1 简介

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

1.2 优先

  1. Spring开源的容器

  2. Spring是轻量级和非入侵式

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

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

1.3 组成

img

1.4 扩展

  1. SpringBoot

    快速开发,约定大于配置

  2. SpringCloud

    基于SpringBoot实现

2. IOC理论推导

  1. UserDao接口
  2. UserDaoImp接口
  3. UserService业务接口
  4. UserServiceImpl业务实现类

使用Set接口实现

public void setUserDao(UserDao userDao) {
    this.userDao = userDao;
}

解决创建对象的问题,耦合性降低

IOC本质:设计思想,DI(依赖注入)是一种实现方式,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 http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--spring创建对象
    UserDaoMysqlImpl mysqlImpl = new UserDaoMysqlImpl()
    id = 变量名
    class = new 的对象
    property 相当于给对象中的属性设置一个值
    -->
    <bean id = "mysqlImpl" class="com.liu.dao.UserDaoMysqlImpl"/>
    <bean id = "oracleImpl" class="com.liu.dao.UserDaoOracleImpl"/>
    <bean id = "sqlServermpl" class="com.liu.dao.UserDaoSqlServermpl"/>


    <bean id="UserServiceImpl" class="com.liu.service.UserServiceImpl">
        <!--ref: 引用spring容器创建的对象
        value: 具体的值,基本数据类型
        -->
        <property name="userDao" ref="sqlServermpl"/>
    </bean>
</beans>

测试代码

import com.liu.service.UserServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");

        UserServiceImpl userServiceImpl = (UserServiceImpl)applicationContext.getBean("UserServiceImpl");

        userServiceImpl.getUser();
    }
}

3. IOC创建对象的方式

  1. 使用无参构造创建对象

    <bean id="user" class="com.liu.pojo.User">
            <property name="name" value="测试"/>
    </bean>
    
  2. 使用有参构造创建对象

    1. 下标赋值

      <!--1.下标赋值-->
      <bean id="user" class="com.liu.pojo.User">
              <constructor-arg index="0" value="测试"/>
      </bean>
      
    2. 参数类型赋值

      <!--2.参数类型赋值,不建议使用-->
      <bean id="user" class="com.liu.pojo.User">
              <constructor-arg type="java.lang.String" value="测试"/>
      </bean>
      
    3. 参数名赋值

      <!--3.通过参数名设置-->
      <bean id="user" class="com.liu.pojo.User">
              <constructor-arg name="name" value="测试"/>
      </bean>
      

总结:在配置文件加载时,容器中管理的对象就创建了

4. Spring配置

4.1 别名

<!--别名,添加别名可以利用别名获取对象-->
<alias name="user" alias="abc"/>

4.2 Bean配置

<!--
    id: bean的唯一标识符,对象名
    class: bean对象对应的全限定名: 包名+类型
    name: 也是别名,可以取多个别名
    -->
<bean id="userT" class="com.liu.pojo.UserT" name="user2, u2">
    <property name="name" value="12"/>
</bean>

4.3 import

利用import将所有人的配置合并成一个总的配置

<import resource="beans.xml"/>

5. 依赖注入

5.1 构造器注入

参考前面方法

5.2 Set方式注入

依赖:bean对象的创建依赖容器

注入:bean对象的所有属性,由容器来注入

对象

public class Address {
    private String address;

    public String getAddress() {
        return address;
    }

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

实际对象

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 Properties info;
    private String wife;
}

配置项

<?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.liu.pojo.Student">
        <!--第一种,普通值注入,value-->
        <property name="name" value="测试"/>
    </bean>
</beans>

测试类

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Student student = (Student)context.getBean("student");
        System.out.println(student.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="address" class="com.liu.pojo.Address"/>

    <bean id="student" class="com.liu.pojo.Student">
        <!--第一种,普通值注入,value-->
        <property name="name" value="测试"/>
        <!--第二种,对象注入,ref-->
        <property name="address" ref="address"/>
        <!--数组注入-->
        <property name="books">
            <array>
                <value>西游记</value>
                <value>三国演义</value>
                <value>水浒传</value>
                <value>红楼梦</value>
            </array>
        </property>
        <!--list注入-->
        <property name="hobbys">
            <list>
                <value>看书</value>
                <value>打球</value>
                <value>玩游戏</value>
            </list>
        </property>
        <!--map注入-->
        <property name="card">
            <map>
                <entry key="身份证" value="12344555"/>
                <entry key="银行卡" value="24124412"/>
            </map>
        </property>
        <!--set注入-->
        <property name="games">
            <set>
                <value>LOL</value>
                <value>CS</value>
            </set>
        </property>
        <!--null-->
        <property name="wife">
            <null/>
        </property>
        <!--properties-->
        <property name="info">
            <props>
                <prop key="driver">1234</prop>
                <prop key="url">男</prop>
                <prop key="username">root</prop>
                <prop key="password">123445</prop>
            </props>
        </property>
    </bean>
</beans>

5.3 拓展方式注入

p空间和c空间

5.4 bean的作用域

  1. singleton(单例模式)--默认
  2. prototype(原型模式)--每次产生新的对象
  3. 其余的request,session,application,只能在web开发用到

6. Bean的自动装配

三种装配的方式

  1. xml显示的装配
  2. 在java中显示的装配
  3. 隐式的自动装配bean

6.1 测试

6.2 使用xml自动装配

 <!--
    byName: 会自动在容器上下文差再,和自己对象set方法后面的值对应的beanId(setDog, setCat)
    byType: 会自动在容器上下文差再,和自己对象属性类型相同的bean
    -->
    <bean id="people" class="com.liu.pojo.People" autowire="byName">
        <property name="name" value="无敌"/>
    </bean>

总结:

  1. byName需要所有的beanId唯一
  2. byType需要所有的bean的class唯一

6.3 使用注解自动装配

使用注解:

  1. 导入约束

  2. 配置注解的支持<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 http://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>
    

@Autowire

直接在属性上使用,也可也在set方法使用,可以省略set方法

@Nullable 字段标记这个注解,说明这个字段可以为Null
public @interface Autowired {
    boolean required() default true;
}

测试代码

public class People {
    //如果显示定义Autowired的required属性为false,说明这个对象可以为null,否则不允许为null
    @Autowired(required = false)
    private Cat cat;
    @Autowired
    private Dog dog;
    private String name;
}

如果装配的环境比较复杂,自动装配无法通过一个注解完成,此时可以采用@Qualifer去配合@Autowired(使用byType)使用,指定一个唯一的bean对象注入

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

@Resource注解

public class People {
    @Resource(name = "cat11")
    private Cat cat;
    @Resource
    private Dog dog;

小结:

@Resource和@Autowired

  1. 都是可以自动装配,都可以放在属性字段上
  2. @Autowired通过byType实现,要求对象存在
  3. @Resource通过byName,如果找不到使用byType实现,两个都找不到报错

7. 使用注解开发

注解开发,必须保证AOP包导入

需要导入context约束

  1. bean

  2. 属性注入

    // 等价于 <bean id = "user" class = "com.liu.pojo.User"/>
    // @Component 组件
    @Component
    public class User {
        public String name;
    
        // 相当于 <property name = "name" value = "测试"/>
        @Value("测试")
        public void setName(String name) {
            this.name = name;
        }
    }
    
  3. 衍生的注解

    @Component有几个衍生注解,按照mvc三层架构分层

    • dao 【@Repository】

    • service 【@Service】

    • controller 【@Controller】

      这四个注解的功能一样,将类注册到spring中,装配Bean

  4. 作用域

    @Component
    @Scope("prototype")
    public class User {
        public String name;
    
        // 相当于 <property name = "name" value = "测试"/>
        @Value("测试")
        public void setName(String name) {
            this.name = name;
        }
    }
    
  5. 小结

    xml与注解:

    • xml更加万能,适用于任何场合,维护方便
    • 注解不是自己类使用不了,维护相对复杂

    xml与注解:

    • xml用来管理Bean
    • 注解只负责完成属性的注入
    • 注解需要开启注解的支持,扫描器的开启

8. 使用Java的方式配置Spring

使用Java来进行配置,JavaConfig在Spring4之后,推荐使用

实体类

public class User {
    private String name;

    public String getName() {
        return name;
    }

    @Value("测试")
    public void setName(String name) {
        this.name = name;
    }

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

配置文件

// 配置类也注册到容器中,类似于之前的xml文件
@Configuration
@ComponentScan("com.liu.pojo")
@Import(MyConfig2.class)
public class MyConfig {

    //注册一个bean,就相当于我们之前的一个bean标签
    //方法的名字就是Bean的id
    //返回对象为Bean的class
    @Bean
    public User getUser() {
        return new User();
    }
}

测试类

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
        User user = (User)context.getBean("getUser");
        System.out.println(user.getName());
    }
}

这种纯java配置,在springboot很常见

9. 代理模式

代理模式分类:

  • 静态代理
  • 动态代理

9.1 静态代理

角色分析:

  • 抽象角色:接口或者抽象类解决
  • 真实角色:被代理的角色
  • 代理角色:代理真实对象,代理真实角色后,一般会做一些附属操作
  • 客户:访问代理对象的人

代码步骤:

  1. 接口

    //租房的接口
    public interface Rent {
        public void rent();
    }
    
  2. 真实角色

    //房东
    public class Host implements Rent{
    
        public void rent() {
            System.out.println("房东要出租房子");
        }
    }
    
  3. 代理角色

    public class Proxy implements Rent{
        private Host host;
    
        public Proxy(Host host) {
            this.host = host;
        }
    
        public Proxy() {
        }
    
        public void rent() {
            host.rent();
        }
    
        //看房
        public void seeHouse() {
            System.out.println("中介带你看房");
        }
    
        //收中介费
        public void fare() {
            System.out.println("中介收中介费");
        }
    
        //签合同
        public void signContract() {
            System.out.println("签约租赁合同");
        }
    }
    
  4. 客户访问代理角色

    public class Client {
        public static void main(String[] args) {
            //房东要租房子
            Host host = new Host();
            //代理,中介帮房东租房子,代理角色有一些附属操作
            Proxy proxy = new Proxy(host);
            //你不用面对房东,直接找中介租房
            proxy.rent();
        }
    }
    

代理模式的好处:

  • 可以使真实角色操作更加纯粹,不用关注一些公共的业务
  • 公共交给代理角色,实现业务的分工
  • 公共业务扩展,方便集中管理

缺点:

  • 一个真实角色就会产生代理角色,代码翻倍,开发效率降低

10.2 动态代理

  • 动态代理和静态代理角色一样
  • 动态代理的代理类动态生产,不是直接写好
  • 动态代理分为两类:基于接口的动态代理,基于类的动态代理
    • 基于接口---JDK动态代理
    • 基于类:cglib
    • java字节码实现:javasist

了解两个类:Proxy:代理,InvocationHandler:调用处理程序

InvocationHandler

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

//自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {

    //被代理的接口
    private Object target;

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

    //生成得到代理类
    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    //处理代理实例,并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //动态代理的本质,就是使用反射机制实现
        Object result = method.invoke(target, args);
        return result;
    }
}

动态代理的好处:

  • 可以使真实角色操作更加纯粹,不用关注一些公共的业务
  • 公共交给代理角色,实现业务的分工
  • 公共业务扩展,方便集中管理
  • 一个动态代理类代理的是一个接口,一般是对应的一类业务
  • 一个动态代理类可以代理多个类,只有实现同一个接口即可
posted @ 2021-02-24 21:35  学徒之心  阅读(52)  评论(0编辑  收藏  举报