Spring框架

目录

概述

一、Spring框架

二、控制反转(IOC)

1、控制反转(IOC)

2、IOC容器

3、依赖注入(DI)

三、面向切面编程(AOP)


概述

Java EE开发最常用的框架是Spring,该框架开始于2003年,它是由罗德·约翰逊(Rod Johnson)创建的一个轻量级开源框架。Spring框架是为了解决企业应用开发的复杂性而创建的,它的出现使得开发者无须开发重量级的Enterprise JavaBean(EJB),而是通过控制反转(IOC)面向切面编程(AOP)的思想进行更轻松的企业应用开发,取代了EJB臃肿、低效的开发模式。

一、Spring框架

Spring框架是一款开源的、轻量级的、基础架构型的开发框架,核心思想是控制反转(IOC)面向切面编程(AOP),为Java应用程序开发提供组件管理服,用于组件之间的解耦,以及简化第三方Java EE中间件技术(JMS、任务调度、缓存、ORM框架)的使用。

Spring框架包括:IOC容器、Validation数据校验、AOP面向切面编程、Transactions事务管理、Spring JDBC、Spring MVC框架、以及各类第三方Java EE中间件技术集成。

Spring框架主要由Core(核心模块)、Testing(测试模块)、Data Access(数据访问模块)、Web Servlet(基于Servlet的Web应用开发)、Integration(企业级系统集成模块)等模块组成。

二、控制反转(IOC)

1、控制反转(IOC)

控制反转(IOC)是Spring框架的核心思想之一,主要用于解耦。IOC是指将创建对象的权利交给Spring进行管理。由Spring框架根据配置文件或注解等方式,创建bean对象并管理各个bean对象之间的依赖关系。是对象之间形成松散耦合的关系,实现解耦。

控制:指创建对象的权力;

反转:控制权交给Spring框架。

 Student类:

public class Student {

}

Test类:

public class Test {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
        Student stu = (Student) applicationContext.getBean("student");
        System.out.println(stu);
    }
}

bean对象: 

<bean id="student" class="com.dytx.info.Student"></bean>

执行结果:

由上述代码的执行结果我们可以切实的体会到Spring框架核心思想之一的控制反转(IOC)。我们不需要通过new关键字去创建对象,而是由Spring框架帮我们创建对象。

2、IOC容器

IOC容器使用ConcurrentHashMap集合存储了BeanDefinition对象,该对象封装了Spring对一个bean所有配置信息,包括:类名、属性、构造方法参数、依赖、是否延迟加载、是否单例等配置信息。

IOC容器我们可以理解为一个Map,key是每个bean对象的id,value是bean对象本身。IOC容器负责创建bean对象并管理bean对象的生命周期。并且根据配置好配置文件或注解,管理IOC容器中的每个bean,以及根据bean之间的依赖关系,完成bean之间的注入。

IOC容器属于Spring Core模块,用来创建和管理bean,默认使用单例的方式将bean存储在DefaultListableBeanFactory类的beanDefinitionMap中(一个ConcurrentHashMap类型的Map集合)。

3、依赖注入(DI)

依赖注入(DI)是对IOC概念的不同角度的描述,是指在运行期间,当前bean对象注入它所依赖的两一个bean对象。例如:有两个类,A类和B类,A类要调用B类中的方法,此时我们不通过new的方式在A类中创建B类的对象,而是通过注入的方式,在A类的bean对象中注入B类的bean对象。

Spring IOC有三种注入方式:set方法注入、构造注入、属性注入。

(1)set方法注入(使用简单的三层模式的串联调用和学生类为例演示)

dao接口和daoImp实现类:

public interface Dao {
    void save();
}

//--------------------------------------------

public class DaoImp implements Dao {
    public void save() {
        System.out.println("---数据访问层---");
    }
}

service接口和serviceImp实现类:

public interface Service {
    void save();
}

//------------------------------------------

public class ServiceImp implements Service{

    Dao dao;

    public void setDao(Dao dao) {
        this.dao = dao;
    }
    
    public void save() {
        System.out.println("---服务层---");
        dao.save();
    }
}

controller接口和controllerImp实现类:

public interface Controller {
    void save();
}

//-----------------------------------------------

public class ControllerImp implements Controller{

    Service service;

    public void setService(Service service) {
        this.service = service;
    }
    
    public void save() {
        System.out.println("---控制层---");
        service.save();
    }
}

Student类:

public class Student {
    private String id;
    private int age;
    private String name;

    public void setId(String id) {
        this.id = id;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

    public String toString() {
        return "Student{" +
               "id='" + id + '\'' +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

Hobby类:(类名是测试时随意起的,大家千万不要!!!)

public class Hobby {
    private List myList;
    private String[] myArray;
    private Map myMap;
    private Set mySet;
    private Properties myPop;

    public void setMyList(List myList) {
        this.myList = myList;
    }

    public void setMyArray(String[] myArray) {
        this.myArray = myArray;
    }

    public void setMyMap(Map myMap) {
        this.myMap = myMap;
    }

    public void setMySet(Set mySet) {
        this.mySet = mySet;
    }

    public void setMyPop(Properties myPop) {
        this.myPop = myPop;
    }
    
    public String toString() {
        return "Hobby{" +
                "myList=" + myList +
                "\n myArray=" + Arrays.toString(myArray) +
                "\n myMap=" + myMap +
                "\n mySet=" + mySet +
                "\n myPop=" + myPop +
                '}';
    }
}

Test类:

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

        Controller controller = (Controller) applicationContext.getBean("controllerImp");
        controller.save();

        Student student = (Student) applicationContext.getBean("student");
        System.out.println(student);

        Hobby hobby = (Hobby) applicationContext.getBean("hobby");
        System.out.println(hobby);
    }
}

bean对象: 

<!-- set方法注入:对象 -->
    <bean id="controllerImp" class="com.dytx.controller.ControllerImp">
        <property name="service" ref="serviceImp"></property>
    </bean>
    <bean id="serviceImp" class="com.dytx.com.dytx.service.ServiceImp">
        <property name="dao" ref="daoImp"></property>
    </bean>
    <bean id="daoImp" class="com.dytx.dao.DaoImp"></bean>

    <!-- set方法注入:基本类型和String -->
    <bean id="student" class="com.dytx.student.Student">
        <property name="id" value="2010044217"></property>
        <property name="name" value="是大眼同学!"></property>
        <property name="age" value="21"></property>
    </bean>

    <!-- set方法注入:List、array、Map、Set、Properties -->
    <bean id="hobby" class="com.dytx.hobby.Hobby">
        <property name="myList">
            <list>
                <value>水浒传</value>
                <value>西游记</value>
                <value>红楼梦</value>
                <value>三国演义</value>
            </list>
        </property>
        <property name="myArray">
            <array>
                <value>孙悟空</value>
                <value>猪八戒</value>
                <value>沙和尚</value>
                <value>唐僧</value>
            </array>
        </property>
        <property name="mySet">
            <set>
                <value>张飞</value>
                <value>赵云</value>
                <value>马超</value>
                <value>关羽</value>
                <value>黄忠</value>
            </set>
        </property>
        <property name="myMap">
            <map>
                <entry key="豹子头" value="林冲"></entry>
                <entry key="霹雳火" value="秦明"></entry>
                <entry key="大刀" value="关胜"></entry>
                <entry key="双鞭" value="呼延灼"></entry>
                <entry key="没羽箭" value="张青"></entry>
            </map>
        </property>
        <property name="myPop">
            <props>
                <prop key="黑旋风">李逵</prop>
                <prop key="小李广">花容</prop>
                <prop key="浪里白条">张顺</prop>
                <prop key="行者">武松</prop>
            </props>
        </property>
    </bean>

执行结果:

 (2)构造注入

Student类:

public class Student {
    String name;
    int age;

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

    public Student() {
    }

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

Test类:

public class Test {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student stu = (Student) applicationContext.getBean("student");
        System.out.println(stu);
    }
}

 bean对象:

    <!--构造方法注入基本数据类型和String-->
    <bean id="student" class="com.dytx.student.Student">
        <constructor-arg name="name" value="是大眼同学"></constructor-arg>
        <constructor-arg name="age" value="21"></constructor-arg>
    </bean>

执行结果:

使用构造方法注入对象与set方法注入对象类似,只需将<bean>下的子标签<property>标签换成<constructor-args>,依然需要使用”ref“属性来标识所注入对象代表的<bean>对象的id。

注意:构造注入不能 给bean对象注入复杂类型。

(3)属性注入:使用成员属性注入bean,不推荐。原因是:使用私有的成员变量属性,依靠反射实现,破坏封装,只能依靠IOC容器实现注入,不严谨。

三、面向切面编程(AOP)

面向切面编程(AOC):将那些与业务无关,却为业务模块所共同调用的逻辑(例如事务处理、日志管理、权限控制等)封装抽取成一个可重用的模块,这个模块被命名为“切面”(Aspect),便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性

Spring AOP是基于动态代理实现的:

 ○ 如果被代理的对象,已经实现某个接口,则 Spring AOP 会使用 JDK Proxy(反射),基于接口的方式,创建代理对象(JDK动态代理的核心是InvocationHandler接口和Proxy类);
 ○ 如果被代理的对象,没有实现某个接口,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 Cglib,基于继承的方式,生成一个被代理对象的子类来作为代理(Cglib动态代理的核心是MethodInterceptor接口和Enhancer类)。

AOP通知类型:AOP将抽取出来的共性功能称为通知。以通知在代码中的具体位置划分为前置通知、返回通知、异常通知、后置通知以及环绕通知。

AOP连接点(Join point):AOP将所有的方法都视为连接点,不管是接口里面的抽象方法,还是实现类里面的重写方法,都是连接点。

AOP切点(Pointcut):AOP将可能被抽取共性功能的方法称为切入点。切入点是连接点的子集。

AOP目标对象(Target): 就是挖掉功能的方法对应的类生成的对象,这种对象是无法直接完成最终工作的。

AOP织入(Weaving):就是将挖掉的功能回填的动态过程。

AOP切面:切点+通知

我们通过实现对业务层加入日志的功能来体会面向切面编程:

IService接口和Service实现类:

public interface IService {
    void insert(String name);
    int delete(int num);
    void select();
}

//--------------------------------------------

public class Service implements IService{
    public void insert(String name) {
        System.out.println("---业务新增---");
    }

    public int delete(int num) {
        System.out.println("---业务删除---");
        return 0;
    }

    public void select() {
        System.out.println("---业务查询---");
    }
}

Logger类: 

public class logger {
    //前置通知
    public void beforMethod() {
        System.out.println("前置通知:" + new Date());
    }

    //返回通知
    public void afterReturnMethod() {
        System.out.println("后置通知:" + new Date());
    }

    //异常通知
    public void throwMethod() {
        System.out.println("异常通知:" + new Date());
    }

    //后置通知
    public void afterMethod() {
        System.out.println("后置通知:" + new Date());
    }
}

Test类:

public class Test {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        IService service = (IService) applicationContext.getBean("service");
        service.insert("dytx");
        service.delete(1);
        service.select();
    }
}

bean对象:

<!--注入Service类-->
    <bean id="service" class="com.dytx.service.Service"></bean>
    <!--注入logger类-->
    <bean id="logger" class="com.dytx.log.logger"></bean>

    <!--开启aop配置-->
    <aop:config>
        <!--切面-->
        <aop:aspect id="qieMian" ref="logger">
            <!--切点-->
            <aop:pointcut id="qieDian" expression="execution(* com.dytx.service.Service.*(..))"/>
            <!--前置通知-->
            <aop:before method="beforMethod" pointcut-ref="qieDian"></aop:before>
            <!--返回通知-->
            <aop:after-returning method="afterReturnMethod" pointcut-ref="qieDian"></aop:after-returning>
            <!--异常通知-->
            <aop:after-throwing method="throwMethod" pointcut-ref="qieDian"></aop:after-throwing>
            <!--后置通知-->
            <aop:after method="afterMethod" pointcut-ref="qieDian"></aop:after>
        </aop:aspect>
    </aop:config>

 执行结果:

从执行结果来看,我们能够切实的体会到面向切面编程的作用,减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。

对于环绕通知,就是将以上四种通知封装到一个返回值Object类型的方法中,在bean对象中使用一个<aop:around>标签即可:

    //环绕通知
    public Object aroundMEthod(ProceedingJoinPoint point){
        Object returnObj = null;
        try {
            System.out.println("环绕通知====>前置通知");
            Object[] obj = point.getArgs();//切点方法的参数
            returnObj = point.proceed(obj);
            System.out.println("环绕通知====>返回通知");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("环绕通知====>异常通知");
        } finally {
            System.out.println("环绕通知====>后置通知");
        }
        return returnObj;
    }
<aop:around method="aroundMEthod" pointcut-ref="qieDian"></aop:around>
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值