依赖注入(DI)和控制反转(IoC)

在日常编码中经常遇到在一个类中调用另一个类的多个方法,所以就在一个类中new出需要用到的类,也就是组合模式。比如说A类里面需要用到很多B类里面的方法,所以要在A类里面new一个B。

public class A {

    private B b = new B();

    public void aa(){
        b.bb();
    }

}

但是这样有个弊端,就是类一开始就定死了,万一业务有变化需要别的实现呢?所以我们要面向接口编程。比如说A类里面需要B类,我们可以将B设计成接口,然后根据业务需求建立不同的B接口实现类B1,B2。。。这样需要什么样的实现就new什么实例。

package com.jyh.test;

public class A {

    private B b = new B1();

    public void aa(){
        b.bb();
    }

public interface B {
    public void bb();
}

public class B1 implements B{

    public void bb() {

    }
}

public class B2 implements B{

    public void bb() {

    }
}

}

但是这样还不好,这样设计如果要改实现还要改A类里面的代码替换B接口的实现类,我们可以将实现丢给调用者来实现,在A类里面定义类型B的属性,外部调用者可以通过setter方法和构造方法给该属性赋值一个实现。

package com.jyh.test;

public class A {

    private B b;

    public A(B b) {
        super();
        this.b = b;
    }

    public B getB() {
        return b;
    }

    public void setB(B b) {
        this.b = b;
    }

    public void aa(){
        b.bb();
    }

public static interface B {
    public void bb();
}

public static class B1 implements B{

    public void bb() {
        System.out.println("b1");
    }
}

public static class B2 implements B{

    public void bb() {
        System.out.println("b2");
    }
}

public static void main(String[] args) {
    A a = new A(new B2());
    a.aa();
}

}

这就是注入,将需要的实现通过某些方法给注入进去,依赖注入就是字面上的意思,因为A类的功能依赖B,所以注入B就称作依赖注入,意思就是依赖的东西的注入。而IoC(控制反转)的意思就是依赖的获取被反转了,本来A依赖B,所以A来创建一个实例,但是现在不是由A创建一个实例,而是别人创建好了实例传递给了A。
Spring容器中IoC部分就是做这种事,由Spring创建实例,由Spring进行注入。

Spring容器中的依赖注入(DI)和控制反转(IoC)

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

    <!-- 
        lazy-init,懒加载,在获取bean的时候才创建实例 
        autowire:自动装配,<property ref/>是指定装配,autowire中byName值就是通过需要注入的属性名字匹配对应id查找bean
        比如说本类中有个名为userDao的属性,那么就在这里查找id为userDao的bean来实例化
    -->
    <bean id="userService" class="com.jyh.service.impl.UserServiceImpl" lazy-init="true" autowire="byName">
        <!-- property表示注入本类中的某个依赖,name为需要注入的依赖(属性)的名字,
            ref指向另一个bean的id,也就是说对应属性的类型,如果是基本类型则就用value直接赋值
            这个注入是setter方法注入 -->
        <property name="userDao" ref="userDao" /><!-- bean -->
    </bean>

     <!-- dao -->
    <bean id="userDao" class="com.jyh.dao.impl.UserDaoImpl"></bean>

    <bean id="user" class="com.jyh.domain.User">
        <!-- 基本类型用value直接赋值 -->
        <property name="name" value="无名"></property>
    </bean>

    <!-- 构造方法注入 -->
    <bean id="userServiceTwo" class="com.jyh.service.impl.UserServiceImpl">
        <!-- 指向参数所带表示的实例bean,顺序按照构造方法参数的顺序 -->
        <constructor-arg ref="userDao"></constructor-arg>
        <!-- 按照参数类型赋值
        <constructor-arg type="java.lang.String" value="构造注入"/> -->
        <!-- 按照参数名赋值
        <constructor-arg name="years" value="7500000"/>
        <constructor-arg name="ultimateAnswer" value="42"/> -->
        <!-- 按照参数顺序赋值
        <constructor-arg index="0" value="7500000"/>
        <constructor-arg index="1" value="42"/> -->
    </bean>

</beans>

注解配置

编写注解前需要在xml配置文件中编写如下配置

<!-- 将该类下的所有类配置成bean,只需要在该包下的类前面加上@Component(value = beanid)注解就可以不用再配置文件中写bean -->
<context:component-scan base-package="com.jyh.annotation"></context:component-scan>

<!-- 启动注解 -->
<context:annotation-config></context:annotation-config>

注解

package com.jyh.annotation;

import javax.annotation.Resource;
/**
 * 注解可以在属性上面也可以在setter方法上面,建议在setter上面
 */

import org.springframework.stereotype.Component;

@Component(value = "b") //有这个注解就代表着在配置文件中注册了bean(等同于bean标签),value值为bean的id,默认为首字母小写的类名
public class B {

    private String name;

    //这两个加一起等同于@Resource(name = "aaa")
    //@Autowired                        //默认按照类型匹配
    //@Qualifier(value = "aaa")         //名称匹配
    private A a;
    public String getName() {
        return name;
    }
    //这个注解代表着这个属性是要通过spring注入来的,等同于bean标签下面的property标签
    //name属性代表着该属性在配置文件中bean的id,也就是@Component()标签中的value,没写默认为属性名
    @Resource(name = "a")
    public void setName(String name) {
        this.name = name;
    }
    public A getA() {
        return a;
    }
    public void setA(A a) {
        this.a = a;
    }

}

面向切面(AOP)

砧板上放了一些面,一刀从中间切下,无论从面的这头到那头还是从那头回到这头都要经过刀面,刀面就是切面。
一个小区里面的人出小区要经过保安,要进小区也要经过保安,保安可以查看你是不是该小区的人,保安就是切面。

面向切面的应用:动态代理

JDK自带的动态代理(借助接口实现的动态代理)

//JDK自带的动态代理实现,利用接口
    @Test
    public void proxy(){
        final UserDao userDao2 = new UserDaoImpl();
        UserDao userDao = (UserDao)Proxy.newProxyInstance(UserDaoImpl.class.getClassLoader(), 
                userDao2.getClass().getInterfaces(), 
                new InvocationHandler() {
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println(proxy.getClass().getName());
                        System.out.println("动态代理");
                        method.invoke(userDao2,args);
                        return null;
                    }
        });
        userDao.say("nnn");
    }

cglib的动态代理(不需要接口的动态代理)

//cglib实现动态代理,不需要接口
    @Test
    public void cglibProxy(){
        final UserDao userDao2 = new UserDaoImpl();
        Enhancer enhancer = new Enhancer();
        //设置拦截器
        enhancer.setCallback(new MethodInterceptor() {

            public Object intercept(Object arg0, Method arg1, Object[] arg2,
                    MethodProxy arg3) throws Throwable {
                arg1.invoke(userDao2, arg2);
                return null;
            }
        });
        //设置传入被代理的类进去作为父类
        enhancer.setSuperclass(userDao2.getClass());
        //获取被代理类
        UserDao userDao = (UserDao)enhancer.create();

        userDao.say("cglib实现动态代理");
    }

Spring容器中的面向切面

xml配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
    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-2.5.xsd
           http://www.springframework.org/schema/aop 
           http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd">

    <context:component-scan base-package="com.jyh.annotation"></context:component-scan>

    <!-- 启动注解 -->
    <context:annotation-config></context:annotation-config>
    <!-- 注解aop -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

    <!-- dao -->
    <bean id="userDao" class="com.jyh.dao.impl.UserDaoImpl"></bean>

    <!-- 切面类 -->
    <bean id="myIntercept" class="com.jyh.aop.MyIntercept"></bean>

    <!-- aop, proxy-target-class="true"设置用cglib实现代理 -->
    <aop:config>
        <!-- 切入点表达式 ,确定目标类,被代理的类,意思就是调用该类中的方法会触发切面 -->
        <aop:pointcut expression="execution(* com.jyh.dao.impl.UserDaoImpl.*(..))"
            id="myProxy" />
        <!-- ref指向的对象就是切面,也就是公共调用的处理方法所在的类 -->
        <aop:aspect ref="myIntercept">

            <!-- 方法调用前的处理,method指向方法,pointcut-ref指向代理对象,也就是上面pointcut元素的id -->
            <aop:before method="beforeMethod" pointcut-ref="myProxy" />

            <!-- 方法调用后的处理 -->
            <aop:after-returning method="afterMethod"
                pointcut-ref="myProxy" returning="val" />

            <!-- 无论有没有成功执行,最终都会调用这个方法 -->
            <aop:after method="finallyMethod" pointcut-ref="myProxy" />

            <!-- 异常通知 -->
            <aop:after-throwing method="exceptionMethod"
                throwing="ex" pointcut-ref="myProxy" />

            <!-- 环绕通知,能控制目标方法执行不执行 -->
            <aop:around method="aroundMethod" pointcut-ref="myProxy" />
        </aop:aspect>
    </aop:config>
</beans>
package com.jyh.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

public class MyIntercept {
    public void beforeMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("连接点的名称(方法名称):"+methodName);
        System.out.println("目标类:"+joinPoint.getTarget().getClass());
        System.out.println("方法调用前");
        System.out.println("-------------------");
    }

    public void afterMethod(JoinPoint joinPoint,Object val){
        System.out.println("目标方法的返回值:" + val);
        System.out.println("方法调用后");
        System.out.println("-------------------");
    }

    public void finallyMethod(){
        System.out.println("最后始终会执行的方法");
        System.out.println("-------------------");
    }

    public void exceptionMethod(JoinPoint joinPoint,Throwable ex){
        System.out.println("异常执行的方法");
        System.out.println(ex.getMessage());
        System.out.println("-------------------");
    }

    public void aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable{
        System.out.println("环绕控制前");
        joinPoint.proceed();//调用目标方法
        System.out.println("环绕控制后");
        System.out.println("-------------------");
    }
}

注解配置

<context:component-scan base-package="com.jyh.annotation"></context:component-scan>

<!-- 启动注解 -->
<context:annotation-config></context:annotation-config>
<!-- 注解aop -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
package com.jyh.annotation;

import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * @Before方法执行前 @AfterReturning方法执行后 @After最终始终都会执行的方法 
 * @AfterThrowing异常执行的方法 @Around环绕
 * @author OverrideRe
 *
 */

@Component("D")
@Aspect                                                         //表示本类是切面
public class D {
    //切入点,由于切入点都是方法,所以这里也定义一个方法作为切入点,没有实际意义
    @Pointcut("execution(* com.jyh.annotation.C.*(..))")        
    private void cc(){};                                        

    @Before("cc()")                                             
    public void before(){
        System.out.println("方法前");
    }

    @AfterReturning("cc()")
    public void after(){
        System.out.println("方法后");
    }
}

Spring整合hibernate和AOP应用之声明式事务

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>

    <session-factory>
        <!-- 链接数据库的用户名 -->
        <property name="connection.username">root</property>
        <!-- 链接数据库的密码 -->
        <property name="connection.password">ying1995520***</property>
        <!-- 链接数据库的驱动 -->
        <property name="connection.driver_class">
            com.mysql.jdbc.Driver
        </property>
        <!-- 链接数据库的url -->
        <property name="connection.url">
            jdbc:mysql://localhost:3306/db3
        </property>

        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>

        <property name="hbm2ddl.auto">update</property>
        <!-- 显示sql语句 -->
        <property name="show_sql">true</property>
        <!-- 格式化的显示sql语句 -->
        <property name="format_sql">true</property>

        <mapping resource="com/jyh/domain/Account.hbm.xml"/>

    </session-factory>
</hibernate-configuration>
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐