2017.4.10 spring-ldap官方文档学习
官网:http://www.springframework.org/ldap
官方文档及例子(重要):http://docs.spring.io/spring-ldap/docs/2.1.0.RELEASE/reference/
JAVA文档(重要):http://docs.spring.io/spring-ldap/docs/2.1.0.RELEASE/apidocs/
GitHub(大量例子):https://github.com/spring-projects/spring-ldap
Spring LDAP Reference
2.基本使用
2.1 使用AttributesMapper进行search和lookup
(1)通过search返回一个属性值
- import static org.springframework.ldap.query.LdapQueryBuilder.query;
- public class PersonRepoImpl implements PersonRepo{
- private LdapTemplate ldapTemplate;
- public void setLdapTemplate(LdapTemplate ldapTemplate){
- this.ldapTemplate = ldapTemplate;
- }
- public List<String> getAllPersonNames(){
- return ldapTemplate.search({
- query().where("objectclass").is("person"),
- new AttributeMapper<String>(){
- public String mapFromAttributes(Attribute attrs)throws NamingException{
- return (String) attrs.get("cn").get();
- }
- }
- }
- });
- }
- }
(2)通过search返回一个Person对象
- package com.example.repo;
- import static org.springframework.ldap.query.LdapQueryBuilder.query;
- public class PersonRepoImpl implements PersonRepo {
- private LdapTemplate ldapTemplate;
- ...
- private class PersonAttributesMapper implements AttributesMapper<Person> {
- public Person mapFromAttributes(Attributes attrs) throws NamingException {
- Person person = new Person();
- person.setFullName((String)attrs.get("cn").get());
- person.setLastName((String)attrs.get("sn").get());
- person.setDescription((String)attrs.get("description").get());
- return person;
- }
- }
- public List<Person> getAllPersons() {
- return ldapTemplate.search(query()
- .where("objectclass").is("person"), new PersonAttributesMapper());
- }
- }
(3)通过lookup返回一个Person对象
在ldap中,有两个"查询"概念,search和lookup。search是ldaptemplate对每一个entry进行查询,lookup是通过DN直接找到某个条目。
"Entries in LDAP are uniquely identified by their distinguished name (DN). If you have the DN of an entry, you can retrieve(找回) the entry directly without searching for it. This is called a lookup in Java LDAP."
在下面的lookup代码中,ldap会跳过为AttributesMapper查找属性。
- package com.example.repo;
- public class PersonRepoImpl implements PersonRepo {
- private LdapTemplate ldapTemplate;
- ...
- public Person findPerson(String dn) {
- return ldapTemplate.lookup(dn, new PersonAttributesMapper());
- }
- }
2.2 创建LDAP Queries
ldap的search 包含许多参数,比如:
- Base LDAP path 基本路径(search应该从LDAP树的哪里开始)
- Search scope 查询范围(search应该进行到LDAP树的哪一层)
- returned attributes要返回的属性
- Search filter 查询过滤器
- package com.example.repo;
- import static org.springframework.ldap.query.LdapQueryBuilder.query;
- public class PersonRepoImpl implements PersonRepo {
- private LdapTemplate ldapTemplate;
- ...
- public List<String> getPersonNamesByLastName(String lastName) {
- LdapQuery query = query()
- .base("dc=261consulting,dc=com")
- .attributes("cn", "sn") //返回的属性
- .where("objectclass").is("person")
- .and("sn").is(lastName);
- return ldapTemplate.search(query, new AttributesMapper<String>() {
- public String mapFromAttributes(Attributes attrs)throws NamingException {
- return attrs.get("cn").get(); //查询每一个entry的"cn"值
- }
- });
- }
- }
2.3 动态创建 Distinguished Names(DN)
为了简化对DN的使用,spring-ldap提供了LdapNameBuilder,和工具类LdapUtils。
假设一个Person有如下的属性:
- Attribute Name Attribute Value
- country Sweden
- company Some Company
- fullname Some Person
(1)使用 LdapNameBuilder 动态创建 LdapName
- package com.example.repo;
- import org.springframework.ldap.support.LdapNameBuilder;
- import javax.naming.Name;
- public class PersonRepoImpl implements PersonRepo {
- public static final String BASE_DN = "dc=example,dc=com";
- protected Name buildDn(Person p) {
- return LdapNameBuilder.newInstance(BASE_DN)
- .add("c", p.getCountry())
- .add("ou", p.getCompany())
- .add("cn", p.getFullname())
- .build();
- }
- ...
(2)用 LdapUtils 获取属性值
- package com.example.repo;
- import org.springframework.ldap.support.LdapNameBuilder;
- import javax.naming.Name;
- public class PersonRepoImpl implements PersonRepo {
- ...
- protected Person buildPerson(Name dn, Attributes attrs) {
- Person person = new Person();
- person.setCountry(LdapUtils.getStringValue(dn, "c"));
- person.setCompany(LdapUtils.getStringValue(dn, "ou"));
- person.setFullname(LdapUtils.getStringValue(dn, "cn"));
- // Populate rest of person object using attributes.
- return person;
- }
2.4 绑定和解绑
在Ldap中,新增与删除叫做绑定和解绑。
2.4.1 新增数据
- package com.example.repo;
- public class PersonRepoImpl implements PersonRepo {
- private LdapTemplate ldapTemplate;
- ...
- public void create(Person p) {
- Name dn = buildDn(p);
- ldapTemplate.bind(dn, null, buildAttributes(p));
- }
- private Attributes buildAttributes(Person p) {
- Attributes attrs = new BasicAttributes();
- BasicAttribute ocattr = new BasicAttribute("objectclass");
- ocattr.add("top");
- ocattr.add("person");
- attrs.put(ocattr);
- attrs.put("cn", "Some Person");
- attrs.put("sn", "Person");
- return attrs;
- }
- }
2.4.2 删除数据
- package com.example.repo;
- public class PersonRepoImpl implements PersonRepo {
- private LdapTemplate ldapTemplate;
- ...
- public void delete(Person p) {
- Name dn = buildDn(p);
- ldapTemplate.unbind(dn);
- }
- }
2.4.3 更新数据
(1)使用 rebind 更新数据
- package com.example.repo;
- public class PersonRepoImpl implements PersonRepo {
- private LdapTemplate ldapTemplate;
- ...
- public void update(Person p) {
- Name dn = buildDn(p);
- ldapTemplate.rebind(dn, null, buildAttributes(p));
- }
- }
(2)使用 modifyAttributes 更新数据
- package com.example.repo;
- public class PersonRepoImpl implements PersonRepo {
- private LdapTemplate ldapTemplate;
- ...
- public void updateDescription(Person p) {
- Name dn = buildDn(p);
- Attribute attr = new BasicAttribute("description", p.getDescription())
- ModificationItem item = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attr);
- ldapTemplate.modifyAttributes(dn, new ModificationItem[] {item});
- }
- }
3.简化 Attribute 的获取和 DirContextAdapter 的操作
3.1 介绍
Java LDAP API 可以注册一个DirContextAdapter来自动创建对象。spring-ldap使用了这个特点,在search和lookup中返回DirContextAdapter实例。
3.2 通过ContextMapper来search 和lookup
任何时候,想要在LDAP数据树中查找entry,spring-ldap都会使用这个entry的DN和Attributes来构建一个DirContextAdapter,这使得我们不再需要使用 AttributesMapper,而是使用ContextMapper来对获取的属性值进行转换。
- package com.example.repo;
- public class PersonRepoImpl implements PersonRepo {
- ...
- private static class PersonContextMapper implements ContextMapper {
- public Object mapFromContext(Object ctx) {
- DirContextAdapter context = (DirContextAdapter)ctx;
- Person p = new Person();
- p.setFullName(context.getStringAttribute("cn"));
- p.setLastName(context.getStringAttribute("sn"));
- p.setDescription(context.getStringAttribute("description"));
- return p;
- }
- }
- public Person findByPrimaryKey(String name, String company, String country) {
- Name dn = buildDn(name, company, country);
- return ldapTemplate.lookup(dn, new PersonContextMapper());
- }
- }
这里特别方便的一点是:当属性具有多值时,可以通过getStringAttributes()来获取。
- private static class PersonContextMapper implements ContextMapper {
- public Object mapFromContext(Object ctx) {
- DirContextAdapter context = (DirContextAdapter)ctx;
- Person p = new Person();
- p.setFullName(context.getStringAttribute("cn"));
- p.setLastName(context.getStringAttribute("sn"));
- p.setDescription(context.getStringAttribute("description"));
- // The roleNames property of Person is an String array
- p.setRoleNames(context.getStringAttributes("roleNames"));
- return p;
- }
- }
3.2.1 AbstactContextMapper
spring-ldap提供了一个ContextMapper的抽象的基础实现类:AbstractContextMapper。自定义的的PersonContextMapper可以这样写:
- private static class PersonContextMapper extends AbstractContextMapper {
- public Object doMapFromContext(DirContextOperations ctx) { //ctx没有用到??
- Person p = new Person();
- p.setFullName(context.getStringAttribute("cn"));
- p.setLastName(context.getStringAttribute("sn"));
- p.setDescription(context.getStringAttribute("description"));
- return p;
- }
- }
3.3 使用DirContextAdapter新增和更新数据
注意新增的时候用的是:DirContextAdapter。更新的时候用的是:DirContextOperations。二者的关系:DirContextAdapter实现了DirContextOperations接口。
3.3.1 新增数据
- package com.example.repo;
- public class PersonRepoImpl implements PersonRepo {
- ...
- public void create(Person p) {
- Name dn = buildDn(p);
- DirContextAdapter context = new DirContextAdapter(dn);
- //和获取一样,set也可以有多值
- context.setAttributeValues("objectclass", new String[] {"top", "person"});
- context.setAttributeValue("cn", p.getFullname());
- context.setAttributeValue("sn", p.getLastname());
- context.setAttributeValue("description", p.getDescription());
- ldapTemplate.bind(context);
- }
- }
3.3.2 更新数据
- package com.example.repo;
- public class PersonRepoImpl implements PersonRepo {
- ...
- public void update(Person p) {
- Name dn = buildDn(p);
- DirContextOperations context = ldapTemplate.lookupContext(dn);
- context.setAttributeValue("cn", p.getFullname());
- context.setAttributeValue("sn", p.getLastname());
- context.setAttributeValue("description", p.getDescription());
- ldapTemplate.modifyAttributes(context);
- }
- }
3.3.3 合并新增和更新数据的代码
从前面两段代码可知,新增和更新有重复的代码,因此合并重复代码,整理如下:
- package com.example.repo;
- public class PersonRepoImpl implements PersonRepo {
- private LdapTemplate ldapTemplate;
- ...
- public void create(Person p) {
- Name dn = buildDn(p);
- DirContextAdapter context = new DirContextAdapter(dn);
- context.setAttributeValues("objectclass", new String[] {"top", "person"});
- mapToContext(p, context);
- ldapTemplate.bind(context);
- }
- public void update(Person p) {
- Name dn = buildDn(p);
- DirContextOperations context = ldapTemplate.lookupContext(dn);
- mapToContext(person, context);
- ldapTemplate.modifyAttributes(context);
- }
- protected void mapToContext (Person p, DirContextOperations context) {
- context.setAttributeValue("cn", p.getFullName());
- context.setAttributeValue("sn", p.getLastName());
- context.setAttributeValue("description", p.getDescription());
- }
- }
3.4 DirContextAdapter和作为属性值的DN
When managing security groups in LDAP it is very common to have attribute values that represent distinguished names. Since distinguished name equality differs from String equality (例如,空格和大小写在DN的判等中是无视的), calculating attribute modifications using string equality will not work as expected.
假设一个member属性值为:cn=John Doe,ou=People。如果代码写作如下,会被认为是两个值,实际上它代表了同一个DN。
- ctx.addAttributeValue("member", "CN=John Doe, OU=People")
要写作如下:
- ctx.addAttributeValue("member", LdapUtils.newLdapName("CN=John Doe, OU=People"))
使用DirContextAdapter来修改group membership:
- public class GroupRepo implements BaseLdapNameAware {
- private LdapTemplate ldapTemplate;
- private LdapName baseLdapPath;
- public void setLdapTemplate(LdapTemplate ldapTemplate) {
- this.ldapTemplate = ldapTemplate;
- }
- public void setBaseLdapPath(LdapName baseLdapPath) {
- this.setBaseLdapPath(baseLdapPath);
- }
- public void addMemberToGroup(String groupName, Person p) {
- Name groupDn = buildGroupDn(groupName);
- Name userDn = buildPersonDn(person.getFullname(),person.getCompany(), person.getCountry());
- DirContextOperation ctx = ldapTemplate.lookupContext(groupDn);
- ctx.addAttributeValue("member", userDn);
- ldapTemplate.update(ctx);
- }
- public void removeMemberFromGroup(String groupName, Person p) {
- Name groupDn = buildGroupDn(String groupName);
- Name userDn = buildPersonDn(person.getFullname(),person.getCompany(),person.getCountry());
- DirContextOperation ctx = ldapTemplate.lookupContext(groupDn);
- ctx.removeAttributeValue("member", userDn);
- ldapTemplate.update(ctx);
- }
- private Name buildGroupDn(String groupName) {
- return LdapNameBuilder.newInstance("ou=Groups").add("cn", groupName).build();
- }
- private Name buildPersonDn(String fullname, String company, String country) {
- return LdapNameBuilder.newInstance(baseLdapPath).add("c", country).add("ou", company).add("cn", fullname).build();
- }
- }
3.5 使用spring-ldap和DirContextAdapter的完整代码
- package com.example.repo;
- import java.util.List;
- import javax.naming.Name;
- import javax.naming.NamingException;
- import javax.naming.directory.Attributes;
- import javax.naming.ldap.LdapName;
- import org.springframework.ldap.core.AttributesMapper;
- import org.springframework.ldap.core.ContextMapper;
- import org.springframework.ldap.core.LdapTemplate;
- import org.springframework.ldap.core.DirContextAdapter;
- import org.springframework.ldap.filter.AndFilter;
- import org.springframework.ldap.filter.EqualsFilter;
- import org.springframework.ldap.filter.WhitespaceWildcardsFilter;
- import static org.springframework.ldap.query.LdapQueryBuilder.query;
- public class PersonRepoImpl implements PersonRepo {
- private LdapTemplate ldapTemplate;
- public void setLdapTemplate(LdapTemplate ldapTemplate) {
- this.ldapTemplate = ldapTemplate;
- }
- public void create(Person person) {
- DirContextAdapter context = new DirContextAdapter(buildDn(person));
- mapToContext(person, context);
- ldapTemplate.bind(context);
- }
- public void update(Person person) {
- Name dn = buildDn(person);
- DirContextOperations context = ldapTemplate.lookupContext(dn);
- mapToContext(person, context);
- ldapTemplate.modifyAttributes(context);
- }
- public void delete(Person person) {
- ldapTemplate.unbind(buildDn(person));
- }
- public Person findByPrimaryKey(String name, String company, String country) {
- Name dn = buildDn(name, company, country);
- return ldapTemplate.lookup(dn, getContextMapper());
- }
- public List findByName(String name) {
- LdapQuery query = query()
- .where("objectclass").is("person")
- .and("cn").whitespaceWildcardsLike("name");
- return ldapTemplate.search(query, getContextMapper());
- }
- public List findAll() {
- EqualsFilter filter = new EqualsFilter("objectclass", "person");
- return ldapTemplate.search(LdapUtils.emptyPath(), filter.encode(), getContextMapper());
- }
- protected ContextMapper getContextMapper() {
- return new PersonContextMapper();
- }
- protected Name buildDn(Person person) {
- return buildDn(person.getFullname(), person.getCompany(), person.getCountry());
- }
- protected Name buildDn(String fullname, String company, String country) {
- return LdapNameBuilder.newInstance()
- .add("c", country)
- .add("ou", company)
- .add("cn", fullname)
- .build();
- }
- protected void mapToContext(Person person, DirContextOperations context) {
- context.setAttributeValues("objectclass", new String[] {"top", "person"});
- context.setAttributeValue("cn", person.getFullName());
- context.setAttributeValue("sn", person.getLastName());
- context.setAttributeValue("description", person.getDescription());
- }
- private static class PersonContextMapper extends AbstractContextMapper<Person> {
- public Person doMapFromContext(DirContextOperations context) {
- Person person = new Person();
- person.setFullName(context.getStringAttribute("cn"));
- person.setLastName(context.getStringAttribute("sn"));
- person.setDescription(context.getStringAttribute("description"));
- return person;
- }
- }
- }
PersonRepoImpl
4. ODM(Object-Directory Mapping)
4.1 介绍
对象-关系映射框架比如Hibernate和JPA,都可以使用注解来将数据库的表关系映射成java的对象。spring-ldap也提供类似的功能。
LdapPerations里有这些方法:
- <T> T findByDn(Name dn, Class<T> clazz)
- <T> T findOne(LdapQuery query, Class<T> clazz)
- <T> List<T> find(LdapQuery query, Class<T> clazz)
- <T> List<T> findAll(Class<T> clazz)
- <T> List<T> findAll(Name base, SearchControls searchControls, Class<T> clazz)
- <T> List<T> findAll(Name base, Filter filter, SearchControls searchControls, Class<T> clazz)
- void create(Object entry)
- void update(Object entry)
- void delete(Object entry)
4.2 注解
每个注解的含义暂时先不详细解释。//todo
- @Entry (required)
- @Id (required)
- @Attribute
- @DnAttribute
- @Transient
4.3 执行
- @Entry(objectClasses = { "person", "top" }, base="ou=someOu")
- public class Person {
- @Id
- private Name dn;
- @Attribute(name="cn")
- @DnAttribute(value="cn", index=)
- private String fullName;
- // No @Attribute annotation means this will be bound to the LDAP attribute with the same value
- private String description;
- @DnAttribute(value="ou", index=0)
- @Transient
- private String company;
- @Transient
- private String someUnmappedField;
- // ...more attributes below
- }
- 24 public class OdmPersonRepo {
- 25 @Autowired
- 26 private LdapTemplate ldapTemplate;
- 27
- 28 public Person create(Person person) {
- 29 ldapTemplate.create(person);
- 30 return person;
- 31 }
- 32
- 33 public Person findByUid(String uid) {
- 34 return ldapTemplate.findOne(query().where("uid").is(uid), Person.class);
- 35 }
- 36
- 37 public void update(Person person) {
- 38 ldapTemplate.update(person);
- 39 }
- 40
- 41 public void delete(Person person) {
- 42 ldapTemplate.delete(person);
- 43 }
- 44
- 45 public List<Person> findAll() {
- 46 return ldapTemplate.findAll(Person.class);
- 47 }
- 48
- 49 public List<Person> findByLastName(String lastName) {
- 50 return ldapTemplate.find(query().where("sn").is(lastName), Person.class);
- 51 }
- 52 }
4.4 ODM和作为属性值的DN
ldap中的安全组通常包含多值属性,每一个属性值都是一个user的DN。这些属性值的处理在前面的 3.4 DirContextAdapter和作为属性值的DN 中提到过。ODM同样有简单的处理办法。
- @Entry(objectClasses = {"top", "groupOfUniqueNames"}, base = "cn=groups")
- public class Group {
- @Id
- private Name dn;
- @Attribute(name="cn")
- @DnAttribute("cn")
- private String name;
- @Attribute(name="uniqueMember")
- private Set<Name> members;
- public Name getDn() {return dn;}
- public void setDn(Name dn) {this.dn = dn;}
- public Set<Name> getMembers() {return members;}
- public void setMembers(Set<Name> members) {this.members = members;}
- public String getName() { return name;}
- public void setName(String name) {this.name = name;}
- public void addMember(Name member) {members.add(member);}
- public void removeMember(Name member) {members.remove(member);}
- }
5.高级LDAP Queries
//todo 暂略。
6.配置
6.1 介绍
采用xml文件方式配置时,需要加入如下xml的命名空间。
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:ldap="http://www.springframework.org/schema/ldap"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd- http://www.springframework.org/schema/ldap
http://www.springframework.org/schema/ldap/spring-ldap.xsd">
6.2 ContextSource 配置
最简单的配置至少需要:username,password,url。完整的属性参看官方文档的说明。
- <ldap:context-source
- username="cn=Administrator"
- password="secret"
- url="ldap://localhost:389" />
6.2.1 DirContextAuthentication
创建并使用DirContext实例时,通常需要认证这些contexts,有不同的配置方式可供选择。
在这一节里(12. User Authentication using Spring LDAP)讨论了使用spring ldap进行用户验证。
authenticated contexts可以默认创建为read-only和read-write两种。在context-source的配置中指定username和password。如果username是一个LDAP 用户的dn,那么不管在context-source中是否配置了base LDAP path,这个user的DN也必须是username。(没看懂)
有些LDAP服务器支持匿名read-only访问。如果需要支持这个功能,将属性anonymous-read-only设置为true即可。
(1)自定义DirContext的认证过程
spring ldap中的默认认证策略是简单的。它将principal(这里是username)和credentials(这里是password)放在哈希表里传递给DirContext的构造函数。这通常是不够的。因此可以在context-source里配置 authentication-strategy-ref,来指定自定义策略。
(2)使用SpringSecurityAuthenticationSource我们前面的配置,直接将username和password写死了。但是一个更常见的场景是,当为某个用户执行LDAP操作时,应该使用的是当前用户的principals和credentials,而不是显式指定。这可以通过配置SpringSecurityAuthenticationSource实现动态配置。
- <beans>
- ...
- <ldap:context-source
- url="ldap://localhost:389"
- authentication-source-ref="springSecurityAuthenticationSource/>
- <bean id="springSecurityAuthenticationSource"
class="org.springframework.security.ldap.authentication.SpringSecurityAuthenticationSource" />- ...
- </beans>
6.2.2 本地java LDAP Pooling//todo
6.2.3 高级ContextSource配置//todo
6.3 LdapTemplate Configuration
- <ldap:ldap-template />
可供配置的属性有:
属性 | 默认值 | 说明 |
id | ldapTemplate | |
context-source-ref | contextSource | 使用的contextSource |
count-limit | 0 | search的默认个数限制,0指的是没有限制。 |
time-limit | 0 | search的默认时间限制,0指的是没有限制。 |
search-scope | SUBTREE | 可选值有:OBJECT,ONELEVEL,SUBTREE |
ignore-name-not-found | false | 指明search时异常NameNotFoundException是否被忽视。 |
ignore-partial-result | false | 指明search时异常PartialResultException是否被忽视。 |
odm-ref | 使用的ObjectDirectoryMapper的bean id。默认是DefaultObjectDirectoryMapper |
6.4 获取base LDAP path的值
通常情况下,在contextSource里定义了base LDAP path,然后操作时都是用的相对路径。但也有某些情况下,需要用到base LDAP path的完整值。比如,操作LDAP groups时,在这里group member的属性值必须是member的完整DN。
step1:实现接口BaseLdapNameAware
- package com.example.service;
- public class PersonService implements PersonService, BaseLdapNameAware {
- ...
- private LdapName basePath;
- public void setBaseLdapPath(LdapName basePath) {
- this.basePath = basePath;
- }
- ...
- private LdapName getFullPersonDn(Person person) {
- return LdapNameBuilder.newInstance(basePath)
- .add(person.getDn())
- .build();
- }
- ...
- }
step2:配置BaseLdapPathBeanPostProcessor
- <beans>
- ...
- <ldap:context-source
- username="cn=Administrator"
- password="secret"
- url="ldap://localhost:389"
- base="dc=261consulting,dc=com" />
- ...
- <bean class="org.springframework.ldap.core.support.BaseLdapPathBeanPostProcessor" />
- </beans>
2017.4.10 spring-ldap官方文档学习的更多相关文章
- Spring 4 官方文档学习(十二)View技术
关键词:view technology.template.template engine.markup.内容较多,按需查用即可. 介绍 Thymeleaf Groovy Markup Template ...
- Spring 4 官方文档学习(十一)Web MVC 框架之配置Spring MVC
内容列表: 启用MVC Java config 或 MVC XML namespace 修改已提供的配置 类型转换和格式化 校验 拦截器 内容协商 View Controllers View Reso ...
- Spring Boot 官方文档学习(一)入门及使用
个人说明:本文内容都是从为知笔记上复制过来的,样式难免走样,以后再修改吧.另外,本文可以看作官方文档的选择性的翻译(大部分),以及个人使用经验及问题. 其他说明:如果对Spring Boot没有概念, ...
- Spring Framework 官方文档学习(四)之Validation、Data Binding、Type Conversion(一)
题外话:本篇是对之前那篇的重排版.并拆分成两篇,免得没了看的兴趣. 前言 在Spring Framework官方文档中,这三者是放到一起讲的,但没有解释为什么放到一起.大概是默认了读者都是有相关经验的 ...
- Spring boot官方文档学习(一)
个人说明:本文内容都是从为知笔记上复制过来的,样式难免走样,以后再修改吧.另外,本文可以看作官方文档的选择性的翻译(大部分),以及个人使用经验及问题. 其他说明:如果对Spring Boot没有概念, ...
- Spring 4 官方文档学习(十一)Web MVC 框架之resolving views 解析视图
接前面的Spring 4 官方文档学习(十一)Web MVC 框架,那篇太长,故另起一篇. 针对web应用的所有的MVC框架,都会提供一种呈现views的方式.Spring提供了view resolv ...
- Spring 4 官方文档学习(十一)Web MVC 框架
介绍Spring Web MVC 框架 Spring Web MVC的特性 其他MVC实现的可插拔性 DispatcherServlet 在WebApplicationContext中的特殊的bean ...
- Spring Framework 官方文档学习(四)之Validation、Data Binding、Type Conversion(二)
接前一篇 Spring Framework 官方文档学习(四)之Validation.Data Binding.Type Conversion(一) 本篇主要内容:Spring Type Conver ...
- Spring Framework 官方文档学习(四)之Validation、Data Binding、Type Conversion
本篇太乱,请移步: Spring Framework 官方文档学习(四)之Validation.Data Binding.Type Conversion(一) 写了删删了写,反复几次,对自己的描述很不 ...
- Spring 4 官方文档学习(十四)WebSocket支持
个人提示:如果需要用到页面推送,高频且要低延迟,WebSocket无疑是最佳选择.否则还是轮询和long polling吧. 做了一个小demo放在码云上,有兴趣的可以看一下,简单易懂:websock ...
随机推荐
- Tomcat学习笔记(五)
生命周期事件 Catalina包含有很多组件.当Catalina启动时,这些组件也会启动,同样,当Catalina关闭时,这些组件也随之关闭,通过实现org.apache.catalina.Lifec ...
- c#网络编程-第一章
1.需求 获得网页数据,并填充到webbrowser空间中 2.代码示例 private void button1_Click_1(object sender, EventArgs e) { //1. ...
- idea创建maven项目需要注意的问题
idea创建maven项目之后,我从deployment中看到报部署错误的问题,下图是解决问题的办法如下图所示:
- 常用sql语句 DML语句
1.select *|字段名 from 表名 [where 条件] [order by 排序 asc|desc] [limit 限制输出 startrow,pagesize] 查询 2.insert ...
- linux下修改mysql数据库编码后无法启动解决办法
linux下老版本的Mysql修改数据库编码的方法是 修改my.cnf vi /etc/my.cnf 在[client]下添加 default-character-set=utf8 在[mysqld] ...
- 信息传递(NOIP2015)(寻找最小环。。)
原题传送门 这是一道寻找最小环的题目. 在做的时候给每一个点染色.. 防止再做已经搜过的点(优化) v[]表示是否访问的过,以及第一次访问该点的时间. u[]表示染色.. 这道题还可以用拓补排序做. ...
- Linux用户态定时器用法以及犯错总结【转】
转自:http://blog.csdn.net/csdn_logo/article/details/48525703 版权声明:本文为博主原创文章,欢迎转载,转载请注明出处,多谢合作. 采样的时候要用 ...
- win2008服务器asp站点配置要点
Win2008服务器重装系统后,运行ASP站点(使用Access数据库)报N多错误,经过一小时总算解决,总结如下: 在win2008服务器上1. 本站点应用程序池改为启用32位.2. 本站点启用父路径 ...
- Hashmap与Hashtable的区别及Hashmap的原理
Hashtable和HashMap有几个主要的不同:线程安全以及速度.仅在你需要完全的线程安全的时候使用Hashtable,而如果你使用Java 5或以上的话,请使用ConcurrentHashMap ...
- 打印PE目录数据信息
printf("数据目录信息:\n"); PIMAGE_DATA_DIRECTORY MyDataDir; MyDataDir = PIMAGE_DATA_DIRECTORY((& ...