开始通过ProdService将用户输入录入数据库……
SpringMVC是建立在Spring基础上的,除了SpringMVC,还有Springboot等框架。
Spring的一个核心功能,就是自动生成对象,或者说将对象的创建权交给Spring管理。
所以我们的代码可以写成这样:
public class RegisterControler {
private IUserService userService; //没有赋值
@RequestMapping(method = RequestMethod.GET)
public ModelAndView input(Model model) {
userService.getByName("fg");
如果没有Spring,userService没有被赋值,就会是null值,程序运行时就会报空指针异常(演示)
@Autowired private IUserService userService;
但什么类型的实例字段呢?演示:运行报错
需要在springmvc-servlet.xml中进行配置:
<bean class="srv.prod.UserService"></bean>
这样,Spring以后碰到IUserService的字段/变量,就会用srv.prod.UserService生成对象!这又被称之为解析(resolve)
@想一想@:freemarker的配置是怎么一回事?
演示:
@AutoWired还可以放置在构造函数、属性、方法参数上……
@Autowired
public RegisterControler(IUserService userService) {
this.userService = userService;
}
实际开发中,会有很多很多需要注入的类,一个一个的配置会非常累。所以SpringMVC为我们提供了一种简便方式:
@Service
public class UserService implements IUserService {
<context:component-scan base-package="srv.prod" />
为了有更好的语义,Spring还提供了:
<context:component-scan base-package="controllers" />
@想一想@:如何利用这个特性快速切换prod和mock service?
从SRV到BLL
演示生成:

因为独立了entity和model,所以必然产生以下转化(映射):
@Override
public UserModel getByName(String name) {
User user = userRepository.GetByName(name);
UserModel userModel = new UserModel();
userModel.setId(user.getId());
userModel.setUsername(user.getUsername());
return userModel;
}
@Override
public int Register(RegisterModel model) {
User user = new User();
user.setUsername(model.getUsername());
user.setPassword(model.getPassword());
userRepository.Save(user);
return user.getId();
}
使用MapStruct的核心是声明这样的接口:
@Mapper
public interface UserConvert {
//接口中的所有字段都是隐式的public,static和final
UserConvert INSTANCE = Mappers.getMapper(UserConvert.class);
User toUser(RegisterModel model);
UserModel toUserModel(User entity);
}
然后直接使用这个接口的INSTANCE字段,调用接口方法:
UserModel userModel = UserConvert.INSTANCE.toUserModel(user);你肯定会奇怪,这个接口都没有实现类的嘛?不,有的!演示:
public class UserConvertImpl implements UserConvert {
@Override
public User toUser(RegisterModel model) {
实现这种效果,依赖于:
<dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>1.4.2.Final</version> </dependency>
<m2e.apt.activation>jdt_apt</m2e.apt.activation>指示激活m2e-apt
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
成功之后:
apt(annotatioin processing tool):它对源代码文件进行检测找出其中的Annotation,根据注解自动生成代码。原理类似于Hibernate Criteria中的jpamodelgen。
//UserConvert INSTANCE = Mappers.getMapper(UserConvert.class);而是在@Mapping上添加属性:
@Mapper(componentModel = "spring")然后利用@AutoWired注解由Spring依赖注且实例对象:
@Autowired private IUserRepository userRepository;默认映射规则(不需要任何额外注解):
Unmapped target properties: "id, inviteCode, invitedBy, invitedCode".
@Mapping()中常用自定义配置:
@Mapping(target = "pwd", source = "password")
User toUser(RegisterModel model); //将toUser()上的规则“倒”过来用 @InheritInverseConfiguration(name = "toUser") RegisterModel toRegister(User entity);
void toArticle(EditModel model, @MappingTarget Article article);和“生成”不同,unmapped字段会被target保留。@想一想@:为什么?对比:
EditModel model = new EditModel();
model.setTitle("model: 飞哥飞哥我爱你");
Article article = new Article();
article.setId(98);
article.setTitle("article:就像老鼠爱大米");
//articleConvert.toArticle(model, article);
article = articleConvert.toArticle(model);
System.out.println(article.getTitle());
System.out.println(article.getId());
@Mapping(target = "id", ignore = true)
@Mappings({
@Mapping(target = "pwd", source = "password"),
@Mapping(target = "id", ignore = true)
})
Spring还集成了数据库访问功能。和ORM(比如Hibernate)集成的组件就是hibernate-orm。
需要通过maven引入:<dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>5.0.2.RELEASE</version> </dependency>其他hibernate相关依赖(包括mysql、连接池、缓存、log等)引入,略
在SpringORM中,引入了LocalContainerEntityManagerFactoryBean类,我们就可以直接(在repository中)通过依赖注入拿到
@Autowired private LocalContainerEntityManagerFactoryBean emFactoryContainer;然后通过LocalContainerEntityManagerFactoryBean就可以获得EntityManagerFactory:
EntityManagerFactory entityManagerFactory = emFactoryContainer.getNativeEntityManagerFactory(); //或者使用getObject()方法 EntityManagerFactory entityManagerFactory = emFactoryContainer.getObject();但从@Autowired就应该知道,LocalContainerEntityManagerFactoryBean是一个需要配置的SpringBean:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="PersistenceXmlLocation" value="/WEB-INF/persistence.xml" /> </bean>简便起见,我们直接利用PersistenceXmlLocation指定hibernate的persistence.xml配置文件路径,完成所有hibernate相关的配置。
演示:entityManagerFactory不会null
SpringMVC(和Hibernate一样)可以通过添加log4j.xml文件放到项目的Java Path目录下,适配log4j。
只是要在我们原来的log4j.xml中添加一个root logger配置才能记录Hibernate以外的信息:
<root level="INFO"> <AppenderRef ref="FileSpring" /> </root>
实际上log4j中,logger按树状结构组织,root就是根节点,所有日志信息都应该录入到root,除非子节点logger上声明了additivity="false"
<Logger name="org.hibernate.SQL" level="debug" additivity="false">演示查看:Spring的日志
@想一想@:每次请求,都会生成一个LocalContainerEntityManagerFactoryBean对象,再生成一个EntityManagerFactory,会不会造成巨大的性能浪费?
这个问题涉及到SpringBean生命周期,可选值包括:
所以LocalContainerEntityManagerFactoryBean默认就是singleton的,不用担心!
演示:
System.out.println("emFactoryContainer:"+ emFactoryContainer);
System.out.println("-----------");
如何改变默认的scope呢?可以通过注解的方式指定
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class Article extends Entity{
注意这里没有直接使用字符串,而是常量。另外两个web项目的scope由WebApplicationContext指定。
或者通过xml:
<bean id="entityManagerFactory" scope="prototype"
但是,比如我们在repository方法中验证,会发现不对劲!即使article的scope已经被设置为prototype,它在每次被调用时仍然是同一个对象?
@想一想@:为什么呢?因为:
我们自始至终都是在调用同一个repository对象的article字段,这个字段当然没法变化!
演示:将Article注入到scope标记为prototype的Controller中:
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class RegisterControler {
那如果我们就要在一个singleton的SpringBean中注入一个prototype的SpringBean呢?
一种办法是通过ApplicationContext的getBean()方法显式的获取SpringBean,触发scope=prototype机制
@Autowired private ApplicationContext context;
article = context.getBean(Article.class);
System.out.println("article:"+article);
还有另外一种方法,就是在指定scope的时候同时指定proxy(代理):
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)演示:proxyMode默认为NO/Default,也就是不使用代理。
所谓“使用代理”,和之前Hibernate的lazy load实现类似,就是:
所以,……
除了不代理(default和no),ScopedProxyMode还有两种:
多快好省!前端后端,线上线下,名师精讲
更多了解 加: