Sping中自定义默认注解名生成器
在类中贴上@Repository等标签,相当于在Spring容器里定义了一个Bean类,这个Bean的名称如果不指定名称,将默认使用当前类名(不含包名)的首字母小写作为Bean的名字.
需求:
要确保这个Bean名称要唯一,显然这样定义的Bean名称太过简单,比较容易造成一样情况,如果能使用全类名作为Bean可防止这样的情况,因为不存在全类名一样的类.例如:org.kirinsoft.frame.AClass
技术实现:
指定定义一个Bean名字生成器,实现Spring提供的BeanNameGenerator,在generateBeanName方法中设计出自己需要的Bean名称.
另外在Spring配置文件中的自动注解配置要指定名字生成器类,如:
<!-- 自动注解配置 --> <context:annotation-config /> <context:component-scan base-package="com.kirinsoft.frame" name-generator="com.kirinsoft.frame.pub.UniqueBeanNameGenerator" />
具体实现方法:
/**
* 唯一Bean名称生成器.以类全名作为Bean名称.
*/
public class UniqueBeanNameGenerator implements BeanNameGenerator{
@Override
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
return definition.getBeanClassName();
}
}
Spring中事务的作用范围
下面一种情景:
在做项目中通常会进行分层架构.假设可分为Service、Biz、Dao3层.关系如下:
一个Service类调用多个Biz类,一个Biz调用多个Dao类完成业务逻辑.
基于这种情况,通常我们把事务控制到Service层.
很当然的引入Spring中配置:(其他配置略)
<property name="beanNames">
<list>
<value>*ServiceImpl</value>
</list>
</property>
那么,引入一个问题:如果同时对Service层和Biz层加入Spring的事务配置中,是哪一层起作用呢、?
换句话说,假设一个Service有两个Biz类,那么第一个Biz类操作成功,而第二个操作失败,此时第一个Biz是否有回滚呢。?
对应的Spring配置:
<property name="beanNames">
<list>
<value>*ServiceImpl</value>
<value>*BizImpl</value>
</list>
</property>
通过实验证明可得到结论:哪个包含的范围大,哪个事务起作用。
上面的假设,Service的事务起作用,第一个Biz会被回滚掉。
Spring事务属性说明
Spring事务属性配置属性说明如下:
|
PROPAGATION_REQUIRED 【支持当前事务,如果当前没有事务,就新建一个事务。(常用)】 PROPAGATION_SUPPORTS 【支持当前事务,如果当前没有事务,就以非事务方式执行】 PROPAGATION_MANDATORY 【支持当前事务,如果当前没有事务,就抛出异常】 PROPAGATION_REQUIRES_NEW 【新建事务,如果当前存在事务,把当前事务挂起】 PROPAGATION_NOT_SUPPORTED 【以非事务方式执行操作,如果当前存在事务,就把当前事务挂起】 PROPAGATION_NEVER 【以非事务方式执行,如果当前存在事务,则抛出异常】 PROPAGATION_NESTED 【如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作】 -Exception表示有Exception抛出时,事务回滚。-代表回滚+就代表提交 readonly 就是read only, 设置操作权限为只读,一般用于查询的方法,优化作用 |
Spring AOP 之 RegexpMethodPointcutAdvisor
昨天,做了有关日志的AOP,对相关的AOP知识总结如下:
1.引入AOP(Aspect Oroented Programming) 面向切面编程,是消除代码重复的一种方法。
2.Spring AOP 中提供了两种PointcutAdvisor,分别是:
①org.springframework.aop.support.RegexpMethodPointcutAdvisor (需要加上完整类名,可以用Spring提供的匹配方式)
②org.springframework.aop.support.NameMatchMethodPointcutAdvisor(只需要方法名,不用加类名)
今天,主要来说明下RegexpMethodPointcutAdvisor的用法。贴一个例子来说明,一些说明都写在注释中~看贴的代码:
/**
* 打印接口
* @author ChenST
*/
public interface IPrinter {
/** 打印接口 */
public void print();
}
/**
* 打印接口实现类
* @author ChenST
*/
@Repository
public class PrinterImpl implements IPrinter{
@Override
public void print() {
System.out.println("hello world");
}
}
/**
* 通知,在执行方法执行后调用该方法
* @author ChenST
*/
//对应的还有MethodBeforeAdvice等
public class AfterPrinter implements AfterReturningAdvice{
//第一个参数表示 切入方法的 [返回对象]
//第二个参数表示 切入方法的 [方法反射对象]
//第三个参数表示 切入方法的 [参数数组(方法的所有参数组成)]
//第四个参数表示 [调用该方法的对象]
@Override
public void afterReturning(Object returnObject, Method method,
Object[] argArray, Object callObject) throws Throwable {
System.out.println("add log:print Hello world");
}
}
<context:annotation-config />
<context:component-scan base-package="com.shine" />
<bean id="printer" class="com.shine.PrinterImpl"/>
<bean id="afterPrinter" class="com.shine.AfterPrinter"/>
<!-- 配置一个拦截器 (切入点对象,确定何时何地调用拦截器) -->
<bean id="pointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!-- [通知,特定连接点所采取的动作] -->
<!-- 加入切面,切面为当执行完print方法后 再执行加入的切面 -->
<property name="advice">
<ref local="afterPrinter"/>
</property>
<!-- 要拦截的方法,可根据Spring提供匹配方式进行拦截 -->
<property name="pattern">
<!-- .表示符合任何单一字元
### +表示符合前一个字元一次或多次
### *表示符合前一个字元零次或多次
### \Escape任何Regular expression使用到的符号
-->
<!-- .*表示前面的前缀(包括包名) 表示print方法-->
<value>.*print</value>
</property>
</bean>
<!-- ### 代理工程 -->
<bean id="proxyFactory" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 指定目标对象,目标对象是PrinterImpl对象 -->
<property name="target">
<ref local="printer"/>
</property>
<!-- 该目标中加入拦截器pointcutAdvisor -->
<property name="interceptorNames">
<list>
<value>pointcutAdvisor</value>
</list>
</property>
</bean>
/**
* 测试类
*/
public class TestMain {
public static void main(String[] args) {
ApplicationContext ctx = new FileSystemXmlApplicationContext(
"classpath:applicationContext.xml");
// ☆注意:这里的Bean对象获取必须从代理工厂中去取,否则无法切入
IPrinter printer = (IPrinter) ctx.getBean("proxyFactory");
printer.print();
}
}
hello world add log:print Hello world
感觉这么多开源东西中,最需要研究的就是Spring了~~ = =|| 继续study~~
No Hibernate Session bound to thread,and configuration does not allow creation错误解决方案
忽遇如下错误:
org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
百度得解:
因:将对应操作入事务即可
方法:于Spring配置文件的事务拦截器中加入对应的类即可~
出现Unable to find Bean with name XXX 可能原因
今天在公司做项目中,
由Ctrl层 >>> Biz层>>> Dao层 。在Ctrl注入Biz层类 ,在Biz层类注入Dao层类,使用注解的方式进行注入,例如,在Biz层注入Dao层的时候,首先需要将Dao层暴露出来,也就是将这个类标上 @Repository等注解,不然可能会出现Unable to find Bean with name的错误。原理实际上就是定义了一个Bean,没有定义的Bean使用注入自然是找不到这个Bean对象咯~~
换句话说若出现:Unable to find Bean with name 这种错误,不妨看看被注入类是否暴露给其他类咯~~也许是这个问题~
Spring中ApplicationContextAware接口用法
加载Spring配置文件时,如果Spring配置文件中所定义的Bean类,如果该类实现了ApplicationContextAware接口,那么在加载Spring配置文件时,会自动调用ApplicationContextAware接口中的
public void setApplicationContext(ApplicationContext context) throws BeansException
方法,并且自动可获得ApplicationContext 对象。前提必须在Spring配置文件中指定改类。
一个Demo程序如下:
Spring配置文件中配置:
<bean id="springContext" class="com.shine.spring.SpringContextHelper"></bean>
/**
* ApplicationContext的帮助类
* 自动装载ApplicationContext
*
* @author ChenST
* @create 2010-6-24
*
*/
public class SpringContextHelper implements ApplicationContextAware {
private static ApplicationContext context ;
/*
* 注入ApplicationContext
*/
@Override
public void setApplicationContext(ApplicationContext context)
throws BeansException {
//在加载Spring时自动获得context
SpringContextHelper.context = context;
System.out.println(SpringContextHelper.context);
}
public static Object getBean(String beanName){
return context.getBean(beanName);
}
}
Spring中抽象类不能注入属性
昨天做项目的时候,因为注入用的很爽,自然而然的在什么类下都用注入,结果出问题,注入的属性都是为null,查了一下,会不会是抽象类的问题呢,百度之,得下面的答案:
|
抽象类不能生成实例对象,spring无法注入 。 因为spring的原理是启动服务器时读取配置文件,取得类名后利用反射机制在spring上下文中生成一个单例的对象,由spring注入属性并维护此对象的状态,抽象类在反射生成对象时就已经失败了,后面的不会进行。 |
Spring2.5常用注解
公司代码很多Spring注解,下午花了点时间学习了下。贴个小案例来使用这些注解:
学习的注解列表
| @Component |
| @Service |
| @Repository |
| @Controller |
| @Autowired |
| @Qualifier |
| @Resource |
| @PostConstruct |
| @PreDestroy |
1.水果接口
/**
* 水果接口
*
* @author ChenST
*
*/
public interface Fruit {
/**
* 获取水果的名字
* @return
*/
public String getName();
}
2.苹果类
/**
* 苹果
* @author ChenST
*
*/
@Service
public class Apple implements Fruit {
/*
* (non-Javadoc)
* @see com.spring.annotation.Fruit#getName()
*/
public String getName() {
return "苹果";
}
}
3.梨类
/**
* 梨
* @author ChenST
*
*/
@Service
public class Pear implements Fruit {
public String getName() {
return "梨";
}
}
4.人类
/**
* 人,拥有水果
* @author ChenST
*
*/
/*
* @Service表示定义个Bean对象默认Bean的名字为
* 类的第一个字母小写名字,例如Human为human
* @Service("name")也可以自己指定名字name
* ----------以下四种注解是等效的,只是意义不同
* @Component 比较中立的类进行注释
* @Service 表示服务层
* @Repository表示持久层
* @Controller表示控制层
*/
@Service
public class Human {
/** 表示拥有水果 */
/*@Autowired(required = false)表示不确定
*Spring容器中拥有某个类的Bean时候,这样Spring
*找不到这个Bean时也不报错
*
*@Autowired注解表示自动注入对象。在配置文件中需要配置
*<context:annotation-config />
*<context:component-scan base-package="*"/>
*
*@Qualifier("apple")表示当这个fruit有两个实现类时
*指定其中一个。负责会报错
*
*@Resource注释与Autowired差不多,Resource可以通过
*name和type来指明要注入的类
*/
//@Autowired
//@Qualifier("apple")
//@Resource(name="apple")
@Resource(type=Apple.class)
private Fruit fruit;
public void setFruit(Fruit fruit) {
this.fruit = fruit;
}
/**
* 吃水果
*/
public void eat(){
System.out.println("我在吃"+this.fruit.getName());
}
/*
* @PostConstruct表示Spring容器初始化后调用
*/
@PostConstruct
public void eatBegin(){
System.out.println("我要开始吃了");
}
/*
* @PreDestroy表示Spring容器销毁之前调用
*/
@PreDestroy
public void eatFinish(){
System.out.println("吃完了");
}
}
5.测试类
/**
* 测试类
*
* @author ChenST
*
*/
public class Test {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
Human human=(Human) ctx.getBean("human");
human.eat();
}
}
5.applicationContext.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
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<!-- 自动注解配置 -->
<context:annotation-config />
<context:component-scan base-package="*" />
</beans>
6.运行结果
我要开始吃了 我在吃苹果 吃完了
Spring中进行测试-Junit框架的扩展
利用Spring来进行测试。首先看看下面几个类。【参照网上的资料】
1.AbstractSpringContextTests 类是针对所有测试情景的类,一般不使用,而是使用他们的之类。
2.AbstractDependencyInjectionSpringContextTests 类继承了AbstractSpringContextTests类是一个针对所有测试的超类,具体依赖于Spring上下文。可支持依赖注入。
3.AbstractTransactionalSpringContextTests 类继承了AbstractDependencyInjectionSpringContextTests类,继承该类的测试用例在spring管理的事务中进行,测试完后对数据库的记录不会造成任何影响。你对数据库进行一些操作后,它会自动把数据库回滚,这样就保证了你的测试对于环境没有任何影响。
测试用例需实现protected abstract String[] getConfigLocations()方法来获取上下文。
1.创建一个测试类的基类
/**
* Spring测试的基类
*
* @author ChenST
*
*/
public abstract class BaseSpringTest extends AbstractDependencyInjectionSpringContextTests {
/**
* 获取上下文spring context,该路径是在classpath下的路径
*/
public String[] getConfigLocations() {
String[] configLocations = { "applicationContext.xml" };
return configLocations;
}
}
2.测试用例
/**
* 测试用例,测试Dao的方法getUserInfo(String userCode)
*
* @author ChenST
*
*/
public class UsersDaoTest extends BaseSpringTest {
/** 用户Dao,注入实现 */
private UsersDAO userDao;
public void setUserDao(UsersDAO userDao) {
this.userDao = userDao;
}
/**
* 测试getUserInfo()方法,需要以test开头
*/
public void testGetUserInfo(){
Users user=userDao.getUserInfo("004");
System.out.println(user.getDepartment().getDepName());
}
}
可通过MyEclipse中的Outline透视图中进行单个方法的测试或者整个类的测试。