Spring入门。

2019-08-16 12:04:51来源:博客园 阅读 ()

新老客户大回馈,云服务器低至5折

Spring入门。

程序的耦合和解耦。

1、问题引入。

在使用jdbc和数据库交互时。注册驱动:DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
如果把mysql的依赖(jar包)删掉,那么程序在编译期就会出现错误。也就是说当前类,依赖于Driver类。

2、程序的耦合。

耦合:简单来说,就是程序间的依赖关系。包括:类之间的依赖、方法之间的依赖。

解耦:降低程序间的依赖关系。

实际开发中应做到:编译期不依赖,运行时才依赖。

之前注册驱动时,一直用的是  Class.forName("com.mysql.cj.jdbc.Driver")。它依赖的只是一个字符串。

所以,

解耦的思路:

  * 第一步:使用反射来创建对象,而 避免使用new关键字。

     问题:这样的话,字符串写死了,如果以后用其他数据库驱动,需要改源码。

  * 第二部:通过读取配置文件来获取要创建的对象全类名。

3、另一个问题。

通过分析,在之前的三层架构中,各个层之间的调用也有很大的耦合问题。

解决方案:工厂模式解耦。

  创建一个 创建Bean对象的工厂。

  Bean:代表可重用组件的含义。

  JavaBean:用Java编写的可重用组件。JavaBean > 实体类。

  第一:需要一个配置文件来配置service和dao。配置的内容:唯一标识=全类名。

  第二:通过读取配置文件的内容,反射创建对象。

public class BeanFactory {
    private static Properties properties;
    static {
        properties=new Properties();
        InputStream is=BeanFactory.class.getClassLoader().getResourceAsStream("beanfactory.properties");
        try {
            properties.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static Object getBean(String beanName) {
        Object bean=null;
        String beanPath=properties.getProperty(beanName);
        try {
            bean=Class.forName(beanPath).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return bean;
    }

}

由于这句代码的存在 bean=Class.forName(beanPath).newInstance();  导致每次用的时候都会创建一个新的对象。

改进方法:将对象先创建完,存入集合中,用的时候直接在集合中拿。、

改进后的工厂模式:

 

public class BeanFactory {
    private static Properties properties;
    private static Map<String,Object> beans;
    static {
        properties=new Properties();
        InputStream is=BeanFactory.class.getClassLoader().getResourceAsStream("beanfactory.properties");
        try {
            properties.load(is);
            beans =new HashMap<String, Object>();
            Enumeration keys = properties.keys();
            while (keys.hasMoreElements()){
                String key=keys.nextElement().toString();
                String beanPath=properties.get(key).toString();
                Object value=Class.forName(beanPath).newInstance();
                beans.put(key,value);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static Object getBean(String beanName) {
       return beans.get(beanName);
    }

}

 

private AcountDao dao=new AcountDaoImp();

private AcountDao dao=(AcountDaoImp)BeanFactory.getBean("AcountDao");

这两种获取对象的形式上,第一种,应用程序直接依赖于资源。而第二种,利用工厂消除了应用程序和资源的直接依赖关系。

第二种方法的思想,就是IOC。

为什么叫控制反转,不叫降低依赖?

  因为,对于应用程序来说,用第一种方式创建对象时,有自己的独立控制权,想要谁就要谁,而第二种方式,应用程序失去了控制器,把控制权交给了工厂和配置文件。这种控制权发生的转移,就是控制反转。

Spring的IOC和DI。

控制反转(Inversion of Control,IOC)把创建对象的权力交给框架,是框架的重要特征。

IOC的作用:削减计算机程序的耦合。

Spring基于xml的IOC的环境搭建:

  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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="AcountDao" class="AcountDao.AcountDaoImp"></bean>
    <bean id="AcountService" class="AcountService.AcountServiceImp"></bean>
</beans>

  3、主程序。

 public static void main(String[] args) {
         //1、获取核心容器对象
        ApplicationContext ac=new ClassPathXmlApplicationContext("bean.xml");
         //2、根据id获取Bean对象
        AcountService acountService=(AcountService)ac.getBean("AcountService");
        AcountDao acountDao=ac.getBean("AcountDao",AcountDao.class);
        System.out.println(acountService);
        System.out.println(acountDao);

    }

ApplicationContext 的三个常用实现类。

  1、ClassPathXmlApplicationContext:它可以加载类路径下的配置文件,要求配置文件必须在类路径下。

  2、FileSystemXmlApplicationContext:它可以加载任意路径下的配置文件。

  3、AnnotationConfigApplicationContext:用于注解创建容器。

核心容器的两个接口。

  1、ApplicationContext:它在构建核心容器时,创建对象采用的策略是立即加载的方式,也就是说,只要一读取完配置文件,马上就创建对象。

  2、BeanFactory:它采用延迟加载的方式,什么时候需要,什么时候创建对象。

Spring对Bean的管理细节。

  1、创建bean 的三种方式。

    * 使用默认构造函数创建:在spring配置文件中使用bean标签,配以id和class属性,且没有其他属性和标签时,采用的就是默认构造函数创建bean对象,此时如果类中没有默认构造函数,则无法创建。

    * 使用普通工厂的方法创建对象(使用某个类的方法创建其他类对象):在bean标签中使用 factory-bean和factory-method 属性指定类和方法。

    * 使用工厂的静态方法创建对象(使用某个类的静态方法创建对象):使用class和factory-method属性。

  2、Bean的作用范围。

    bean标签的scope属性取值:

      * singleton:单例的(默认值) 常用

      * prototype:多例的。常用

      * request:作用于web应用的请求范围。

      * session:作用于web应用的会话范围。

      * global-session:作用于集群环境的会话范围。

  3、Bean对象的生命周期。

    * 单例对象。

      出生:当容器创建时对象出生。

      活着:只要容器存在,对象就一直存在。

      死亡:容器销毁,对象销毁。

      总结:单例对象的生命周期和容器相同。

    * 多例对象。

      出生:要使用对象时创建。

      活着:对象 只要在使用过程中就一直活着。

      死亡:对象长时间不用,且没有被引用时,由jvm回收。

Spring中的依赖注入。

IOC的作用:降低程序间的依赖关系(耦合)

依赖关系的管理:以后都交给spring来维护。

在当前类需要用到其他类的对象时,由spring为我们提供,我们只需在配置文件中说明。

依赖关系的维护就称之为依赖注入。

依赖注入:

  1、能注入的数据,有三类:

    *基本类型和String

    *其他bean类型(在配置文件中或注解配置过的bean)

    *复杂类型/集合类型

  2、注入的方式,有三种:

    第一种:使用构造函数提供

      使用的标签:constructor-arg

      标签的位置:bean里

      标签中的属性:

        * type:指定注入数据的类型,也是构造函数中参数的类型。

        * index:要注入的数据在构造函数中的位置。

        * name:构造函数中指定参数。

        * value:用于提供基本类型和String类型的数据。

        * ref:用于指定其他的bean类型数据。

<bean id="AcountService" class="AcountService.AcountServiceImp">
        <constructor-arg name="name" value="rt"></constructor-arg>
        <constructor-arg name="age" value="21"></constructor-arg>
        <constructor-arg name="birthday" ref="birthday"></constructor-arg>
    </bean>
    <bean id="birthday" class="java.util.Date"></bean>

      优势:获取bean对象时,注入数据是必须的操作。

      弊端:改变了bean对象的实例化方式,如果创建对象时用不到这些数据,也必须提供。

    第二种:使用set方法提供    更常用

      涉及的标签:property

      出现的位置:bean里

      标签的属性:

        * name:指定注入时所调用的set方法名称。

        * value:用于提供基本类型和String类型的数据。

        * ref:用于指定其他的bean类型数据。

      优势:创建对象时没有明确的限制,可以直接使用默认的构造函数。

      弊端:如果某个成员必须有值,则获取对象时有可能set方法没有执行。

 

      复杂类型/集合类型的注入:

        *用于给list结构的集合注入的标签:list   arry  set

        *用于给map结构的集合注入的标签:map  props

    第三种:使用注解提供

      用于创建对象的注解: 它们的功能和<bean>标签实现的功能一样

        1、@Component:把当前类对象存入spring容器中。

        属性:value:用于指定bean的id。默认是当前类名,且首字母小写。

        2、@Controller:一般用于表现层

        3、@Service:一般用在业务层

        4、@Repository:一般用在持久层

        以上三个注解的作用和@Componet一样,是Spring为我们提供的明确三层使用的注解,

        使我们的三层对象更加清晰。

        需要的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">

    <!--告知spring在创建容器时要扫描的包,配置所需的标签不是在beans的约束中,而是
    一个名称为context名称空间和约束中-->
    <context:component-scan base-package="AcountService"></context:component-scan>

</beans>

       用于注入数据的注解:它们的作用和bean中的<property>标签的作用一样

        1、@Autowired:

          * 作用:自动按类型注入。

              * 只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以成功注入。

              * 如果ioc容器中没有任何bean的类型和要注入的变量类型匹配,则报错。

              * 如果ioc容器中有多个匹配的类型时,在将变量名作为bean的id,继续查找匹配项。

          * 出现位置:变量上,或者方法上。

        2、@Qualifier:

          * 作用:在按照类型注入的基础上再按照名称注入。它在给类成员注入时不能单独使用(需要AotoWired)。但是在给方法参数注入时可以。

          * 属性:value:用于指定注入bean的id。

        3、@Resource:

          * 作用:直接按照bean的id注入,可以单独使用。

          * 属性:name:bean的id。

        4、@Value:

          * 作用:用于注入基本类型和String类型的数据。

          * 属性:value:用于指定数据的值。可以使用spring的SpEL表达式。${表达式}

      用于改变作用范围的注解:它们的作用和bean中的scope属性作用一样

        1、@Scope:

          * 作用:指定bean的作用范围。

          * 属性:value:指定范围的取值:singleleton   prototype

      和生命周期相关的注解:它们的作用和bean中的init-method  和 destroy-method  属性的作用一样

        1、@PreDestroy

          * 作用:用于指定销毁方法

        2、@PostConstruct

          * 作用:用于指定初始化方法

Spring中的新注解:

虽然上面的描述中已经使用了注解来配置,但是任然需要xml文件。

解决方案:创建一个配置类。

  1、@Configuration

    * 作用:指定当前类是一个配置类。

  2、@ComponetScan

    * 作用:指定spring在创建容器时需要扫描的包

    * 属性:value:它和basePackeages的作用一样,都用于指定创建容器时需要扫描的包,相当于xml的:<context:component-scan base-package="AcountService"></context:component-scan>

  3、@Bean

    * 作用:将当前方法的返回值作为bean对象存入ioc容器。

    * 属性:name:指定bean 的id,默认是方法名称。

    * 细节:使用注解配置方法时,如果方法有参数,spring会在容器中查找有没有可用的bean对象,查找方式和Aoutowired一样。

  4、@Import

    * 作用:用于导入其他配置类。

    * 属性:value:指定其他配置类的字节码。、

       使用Import注解的类是父配置类,导入的是子配置类。

  5、@PropertySource

    * 作用:用于指定properties文件的位置。

    * 属性:value:文件的名称和路径。 关键字:classpath:表示类路径下。

Spring整合Junit的配置。

  1、导入jar坐标。

  2、使用Junit提供的一个注解把原有的main方法替换成spring提供的。

    @Runwith(SpringJUnit4ClassRunner.clsss)

  3、告知spring的运行器,spring和ioc创建是基于xml还是基于注解,且说明位置。

    @ContextConfiguration

      location:指定xml文件的位置,加上classpath,表示在类路径下。

      classes:指定注解类所在位置。

  当使用spring5.x时,要求junit必须是4.12及以上。

AOP:

动态代理:

  特点:字节码随用随创建,随用随加载.

  作用:不修改源码的基础上对方法增强.

  分类:基于接口的动态代理   基于子类的动态代理.

  基于接口的动态代理:

    1.涉及的类:Proxy

    2.提供者:JDK

    3.如何创建代理对象:newProxyInstance方法.

    4.创建代理对象的要求:被代理类最少实现一个接口.

    5.newProxyInstance方法的参数:

      * ClassLoader:类加载器. 用于加载代理对象的字节码,和被代理对象使用相同的类加载器. 固定写法.

      * Class[] :字节码数组. 用于让代理对象和被代理对象有相同的方法. 固定写法.

      * InvocationHandler: 用于提供增强的代码. 它是让我们写如何代理. 一般都是写一个该接口的实现类,通常是匿名内部类,但不是必须的.

    6.invoke方法的作用: 执行被代理对象的任何接口方法都经过改方法(拦截作用)

    7.invoke方法的参数:

      * proxy:代理对象的引用.

      * method: 当前执行的方法.

      * args :当前执行方法所需的参数.

      * 返回值: 和被代理对象方法有相同的返回值.

问题引入:

  在转账案例中,分析可知,应该使用ThreadLocal对象把Connection和当前线程绑定,从而使一个线程只能有一个能控制事务的对象。

AOP概述:

AOP:Aspect Oriented Programming 。面向切面编程。

作用:在程序运行期间,不修改源码对已有的方法进行增强。

优势:减少重复代码,提高开发效率,维护方便。

实现方式:动态代理。

Spring中的AOP:spring中的AOP就是通过配置的方式,实现AOP功能 。

  1、引入依赖。

  2、添加配置文件<aop:aspectj-autoproxy>自动生成代理

  3、写切面类(重复的模块),并用@Component  @Aspect

  4、申明各种通知。可以在通知方法中声明类型为 JoinPoint的参数,用来访问被代理方法的参数。


原文链接:https://www.cnblogs.com/zhangyuhao/p/11239687.html
如有疑问请与原作者联系

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:Maven 教程之 pom.xml 详解

下一篇:Java学习2-基本语法