核心功能
IOC(控制反转)和AOP(面向切面编程)
一、IOC
控制反转指的是将工程中的各层对象反转交给spring来创建管理。
DI(依赖注入)是IOC的具体实现方法(Spring在创建一个类的实例时,如果这个类的里面依类型赖其他的引用,
那么会自动将其引用的类型的类先实例化再注入到该实例里面)
1.配置xml文件的方式
spring核心配置文件
<!-- 无参构造 -->
<bean id="userDao" class="daoImpl.UserDaoImpl"></bean>
<bean id="userService" class="serviceImpl.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
<!-- 如果需要创建的对象是有参的构造方法,那么必须得使用下面这种方式进行创建 -->
<bean id="testService" class="serviceImpl.TestServiceImpl">
<constructor-arg name="name" value="admin"></constructor-arg>
<constructor-arg name="url" value="http://localhost"></constructor-arg>
</bean>
<!-- 通过静态工厂构造对象 (不常用)-->
<bean id="userDao1" class="factory.UserDaoFactory" factory-method="getUserDao"></bean>
</beans>
2.通过注解方式(此方法配置内容少,此处用注解方法开发代替1的xml配置)
配置context:component-scan组件扫描(),在核心配置文件配置
原理:
在加载spring容器时,根据上边配置的包路径,扫描包下的及子包下的类,
如果标识了:@controller、@Service、@Repository、@Component进行实例化。
<context:component-scan base-package="cn.edu360.spring"></context:component-scan>
@Repository:标识此bean为一个dao
@Service 标识bean为service
@controller 标识bean为控制器
在spring中提供了@component标识就是一个bean。
@Component可以用在任何的bean上。
依赖注入的注解
Resource注解是jdk下的(该注解是按bean 的ID 值来查找,没有的话再按类型)
autoWired是spring提供(该注解按bean类型来查找,通常要用组合方法,如下面所述,比较繁琐)
使用时@Qualifier和@Autowired组合配置
如:@Autowired//按类型注入
@Qualifier("customerDao")//指定bean的id或name
private CustomerDao customerDao;
在这里推荐使用的是@Resource(name="此处为bean的ID")
**在注入bean所依赖的其它类型时,要提供set方法把对象注入(也可以把@Resource注解写到该方法上)
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
3.获取bean的方式
//加载核心配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
//通过context容器拿到需要的对象
TestService testService = (TestService)context.getBean("testService");
getBean后面也可以获取通过 接口实现类.class获取 如(TestService)context.getBean(testServiceImpl.class)
二、AOP
1.动态代理
当原有的业务类满足不了需求时,可以通过定义动态代理类对原有业务方法进行前置或后后置的扩展
* 注意事项::需要反射执行原有的业务类的业务方法
*
* 动态 可以给任意接口生成代理类对象,扩充原接口的业务方法
* 代理 需要在代理类的执行逻辑当中通过反射机制反射执行原有的业务类的业务方法
public static void main(String[] args) throws InterruptedException {
// 通过动态代理类创建一个动态代理的对象(被代理类的信息,重新定义动态代理的执行行为)
ComputerService daili =
(ComputerService) Proxy.newProxyInstance (ComputerServiceImpl.class.getClassLoader(), ComputerServiceImpl.class.getInterfaces(),new InvocationHandler() {
//动态代理类对象需要自己定义的实现逻辑
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//原有的业务方法之前或之后加一些扩充逻辑
//前置扩展
if(method.getName().equalsIgnoreCase("start")){
System.out.println("电源插上电源线");
}
//原有的业务方法
ComputerServiceImpl impl = new ComputerServiceImpl();
Object resultObject = method.invoke(impl);
//后置扩展
if(method.getName().equals("halt")){
System.out.println("拔掉电源线");
}
return resultObject;
}
});
daili.start();
Thread.sleep(5000);
daili.halt();
}
2.反射机制
/*
* 反射执行computerServiceImpl的start方法
*/
public static void main(String[] args) throws ClassNotFoundException, Exception, IllegalAccessException {
//得到clazz对象
Class clazz = Class.forName("serviceImpl.ComputerServiceImpl");
//反射得到原类型的对象
Object object = clazz.newInstance();
//得到start这个method对象
Method method = clazz.getDeclaredMethod("start");
//反射执行method
method.invoke(object);
}
3.aop
原理举例:
配置文件中:
<bean id="userService" class = "service.impl.UserServiceImpl"></bean>
<bean id="advice" class="advice.UserServiceAdvice"></bean>
<!-- aop配置 -->
<aop:config>
<!-- 切面配置 -->
<aop:aspect ref="advice">
<!-- 切点 -->
<aop:pointcut expression="execution(* service.*.*(..))" id="myPoint"/>
<!-- 切点前置增强 -->
<aop:before method="before" pointcut-ref="myPoint"/>
<!-- 切点后置增强 -->
<aop:after method="after" pointcut-ref="myPoint"/>
</aop:aspect>
</aop:config>
userServiceAdvice类写具体前置后置方法
//前置增强
public void before(){
System.out.println("我通过sqlsessionfactory创建了一个session对象");
}
//后置增强
public void after(){
System.out.println("session对象提交事务");
}
方法的调用
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService)context.getBean("userService");
userService.findUserById();
//为什么调用原有的service业务方法,springaop通过配置增强?
/*
* 因为通过aop的配置指定了切点和切面,还有切面的增强类
* spring提供了方法的拦截器拦截了切点配置的这些类当中的方法,
* 当运行原有的userService.findUserById()方法时
* 方法的拦截器拦截了这个请求,通过动态代理的机制构建了userService的动态代理对象
* invoke方法的实现
* 先调用了增强类的前置增强方法,
* 再调用原有业务类的业务方法,再调用增强类的后置增强方法
*
*/
4.aop的具体应用
事务管理器的配置
事务管理器的作用相当于3中的例子,比如在删除用户的操作中,我们不仅仅要删去用户,也要删去其相关角色等表的信息
那么我们在删的时候如果前一个删,后一个出错,那么我们就要将其都回滚到原始状态,即要么都成功操作,要么都不操作。
在实际配置中在spring-mybatis.xml中配置
<!-- 事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 数据源 -->
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 通知:增强 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 传播行为 -->
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="insert*" propagation="REQUIRED" />
<tx:method name="delete*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="find*" propagation="SUPPORTS" read-only="true" />
<tx:method name="get*" propagation="SUPPORTS" read-only="true" />
</tx:attributes>
</tx:advice>
<!-- 切面 -->
<aop:config>
<aop:advisor advice-ref="txAdvice"
pointcut="execution(* com.dohit.ssm.service.*.*(..))" />
</aop:config>
这是用xml配置,(其它可用注解方式)
当配置了该部分后,spring就会用动态代理的方式拿到service接口类的对象,调用service层的具体实现方法,
如果没有动态代理则,事务管理器不会生效
在实现测试中发现并不能用,原因如下
由于springmvc的配置文件与spring的配置文件不是同时加载,如果这边不进行这样的设置,
那么,spring就会将所有带@Service注解的类都扫描到容器中,等到加载spring的配置文件的时候,会
因为容器已经存在Service类,使得cglib将不对Service进行代理,
直接导致的结果就是在spring配置文件中的事务配置不起作用,
所以在spring配置文件中,添加不扫描controler,如下:
<!-- 自动扫描组件,这里要把controler下面的 controller去除,如果不去除会影响事务管理的。 -->
<context:component-scan base-package="com.weituo">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
在springMVC中,添加不扫描service,如下:
<context:component-scan base-package="com.weituo">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" />
经过测试成功